diff --git a/app/src/components/layout/scenes/MainScene.tsx b/app/src/components/layout/scenes/MainScene.tsx index 6e49fe4..117d7d6 100644 --- a/app/src/components/layout/scenes/MainScene.tsx +++ b/app/src/components/layout/scenes/MainScene.tsx @@ -33,7 +33,7 @@ import { import { useProductContext } from "../../../modules/simulation/products/productContext"; import RegularDropDown from "../../ui/inputs/RegularDropDown"; import RenameTooltip from "../../ui/features/RenameTooltip"; -import { setAssetsApi } from "../../../services/factoryBuilder/assest/floorAsset/setAssetsApi"; +import { setAssetsApi } from "../../../services/factoryBuilder/asset/floorAsset/setAssetsApi"; import { useParams } from "react-router-dom"; import { useSceneContext } from "../../../modules/scene/sceneContext"; import { useVersionHistoryStore } from "../../../store/builder/useVersionHistoryStore"; diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index 391675b..0420d2a 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; import Search from "../../ui/inputs/Search"; -import { getCategoryAsset } from "../../../services/factoryBuilder/assest/assets/getCategoryAsset"; +import { getCategoryAsset } from "../../../services/factoryBuilder/asset/assets/getCategoryAsset"; import { fetchAssets } from "../../../services/marketplace/fetchAssets"; import { useSelectedItem } from "../../../store/builder/store"; diff --git a/app/src/components/layout/sidebarRight/SideBarRight.tsx b/app/src/components/layout/sidebarRight/SideBarRight.tsx index 5fbf079..1fa90d3 100644 --- a/app/src/components/layout/sidebarRight/SideBarRight.tsx +++ b/app/src/components/layout/sidebarRight/SideBarRight.tsx @@ -1,27 +1,28 @@ import React, { useEffect } from "react"; import Header from "./Header"; import useModuleStore, { - useSubModuleStore, + useSubModuleStore, } from "../../../store/useModuleStore"; import { - AnalysisIcon, - MechanicsIcon, - PropertiesIcon, - SimulationIcon, + AnalysisIcon, + MechanicsIcon, + PropertiesIcon, + SimulationIcon, } from "../../icons/SimulationIcons"; import { useToggleStore } from "../../../store/useUIToggleStore"; import Visualization from "./visualization/Visualization"; import Analysis from "./analysis/Analysis"; import Simulations from "./simulation/Simulations"; import useVersionHistoryVisibleStore, { - useSaveVersion, - useSelectedFloorItem, - useToolMode, + useSaveVersion, + useSelectedFloorItem, + useToolMode, } from "../../../store/builder/store"; import { - useSelectedEventData, - useSelectedEventSphere, + useSelectedEventData, + useSelectedEventSphere, } from "../../../store/simulation/useSimulationStore"; +import { useBuilderStore } from "../../../store/builder/useBuilderStore"; import GlobalProperties from "./properties/GlobalProperties"; import AssetProperties from "./properties/AssetProperties"; import ZoneProperties from "./properties/ZoneProperties"; @@ -29,215 +30,227 @@ import EventProperties from "./properties/eventProperties/EventProperties"; import VersionHistory from "./versionHisory/VersionHistory"; import AisleProperties from "./properties/AisleProperties"; import WallProperties from "./properties/WallProperties"; -import { useBuilderStore } from "../../../store/builder/useBuilderStore"; +import FloorProperties from "./properties/FloorProperties"; import SelectedWallProperties from "./properties/SelectedWallProperties"; -import { useSceneContext } from "../../../modules/scene/sceneContext"; +import SelectedFloorProperties from "./properties/SelectedFloorProperties"; const SideBarRight: React.FC = () => { - const { activeModule } = useModuleStore(); - const { toggleUIRight } = useToggleStore(); - const { toolMode } = useToolMode(); - const { subModule, setSubModule } = useSubModuleStore(); - const { selectedFloorItem } = useSelectedFloorItem(); + const { activeModule } = useModuleStore(); + const { toggleUIRight } = useToggleStore(); + const { toolMode } = useToolMode(); + const { subModule, setSubModule } = useSubModuleStore(); + const { selectedFloorItem } = useSelectedFloorItem(); + const { selectedWall, selectedFloor, selectedAisle } = useBuilderStore(); + const { selectedEventData } = useSelectedEventData(); + const { selectedEventSphere } = useSelectedEventSphere(); + const { viewVersionHistory, setVersionHistoryVisible } = useVersionHistoryVisibleStore(); + const { isVersionSaved } = useSaveVersion(); - const { selectedWall } = useBuilderStore(); - const { selectedEventData } = useSelectedEventData(); - const { selectedEventSphere } = useSelectedEventSphere(); - const { viewVersionHistory, setVersionHistoryVisible } = useVersionHistoryVisibleStore(); - const { isVersionSaved } = useSaveVersion(); + // Reset activeList whenever activeModule changes + useEffect(() => { + if (activeModule !== "simulation") setSubModule("properties"); + if (activeModule === "simulation") setSubModule("simulations"); + }, [activeModule, setSubModule]); + useEffect(() => { + if ( + activeModule !== "mechanics" && + selectedEventData && + selectedEventSphere + ) { + setSubModule("mechanics"); + } else if (!selectedEventData && !selectedEventSphere) { + if (activeModule === "simulation") { + setSubModule("simulations"); + } + } + if (activeModule !== "simulation") { + setSubModule("properties"); + } + }, [activeModule, selectedEventData, selectedEventSphere, setSubModule]); - - // Reset activeList whenever activeModule changes - useEffect(() => { - if (activeModule !== "simulation") setSubModule("properties"); - if (activeModule === "simulation") setSubModule("simulations"); - }, [activeModule, setSubModule]); - - useEffect(() => { - if ( - activeModule !== "mechanics" && - selectedEventData && - selectedEventSphere - ) { - setSubModule("mechanics"); - } else if (!selectedEventData && !selectedEventSphere) { - if (activeModule === "simulation") { - setSubModule("simulations"); - } - } - if (activeModule !== "simulation") { - setSubModule("properties"); - } - }, [activeModule, selectedEventData, selectedEventSphere, setSubModule]); - - return ( -
-
- {toggleUIRight && ( - <> - {!isVersionSaved && ( -
- {activeModule !== "simulation" && ( - - )} - {activeModule === "simulation" && ( + return ( +
+
+ {toggleUIRight && ( <> - - - + {!isVersionSaved && ( +
+ {activeModule !== "simulation" && ( + + )} + {activeModule === "simulation" && ( + <> + + + + + )} +
+ )} + + {viewVersionHistory && ( +
+
+ +
+
+ )} + + {/* process builder */} + {!viewVersionHistory && + subModule === "properties" && + activeModule !== "visualization" && + !selectedFloorItem && + !selectedFloor && + !selectedWall && ( +
+
+ {(() => { + if (toolMode === "Aisle") { + return ; + } else if (toolMode === "Wall") { + return ; + } else if (toolMode === "Floor") { + return ; + } else { + return ; + } + })()} +
+
+ )} + + {!viewVersionHistory && + subModule === "properties" && + activeModule !== "visualization" && + selectedFloorItem && ( +
+
+ +
+
+ )} + + {!viewVersionHistory && + subModule === "properties" && + activeModule !== "visualization" && + !selectedFloorItem && + !selectedFloor && + !selectedAisle && + selectedWall && ( +
+
+ +
+
+ )} + + {!viewVersionHistory && + subModule === "properties" && + activeModule !== "visualization" && + !selectedFloorItem && + !selectedWall && + !selectedAisle && + selectedFloor && ( +
+
+ +
+
+ )} + + {!viewVersionHistory && + subModule === "zoneProperties" && + (activeModule === "builder" || activeModule === "simulation") && ( +
+
+ +
+
+ )} + + {/* simulation */} + {!isVersionSaved && + !viewVersionHistory && + activeModule === "simulation" && ( + <> + {subModule === "simulations" && ( +
+
+ +
+
+ )} + {subModule === "mechanics" && ( +
+
+ +
+
+ )} + {subModule === "analysis" && ( +
+
+ +
+
+ )} + + )} + {/* realtime visualization */} + {activeModule === "visualization" && } - )} -
- )} - - {viewVersionHistory && ( -
-
- -
-
- )} - - - - {/* process builder */} - {!viewVersionHistory && - subModule === "properties" && - activeModule !== "visualization" && - !selectedFloorItem && - !selectedWall && ( -
-
- {(() => { - if (toolMode === "Aisle") { - return ; - } else if (toolMode === "Wall") { - return ; - } else { - return ; - } - })()} -
-
)} - - {!viewVersionHistory && - subModule === "properties" && - activeModule !== "visualization" && - selectedFloorItem && ( -
-
- - -
-
- )} - - {!viewVersionHistory && - subModule === "properties" && - activeModule !== "visualization" && - !selectedFloorItem && - selectedWall && ( -
-
- -
-
- )} - - {!viewVersionHistory && - subModule === "zoneProperties" && - (activeModule === "builder" || activeModule === "simulation") && ( -
-
- -
-
- )} - - {/* simulation */} - {!isVersionSaved && - !viewVersionHistory && - activeModule === "simulation" && ( - <> - {subModule === "simulations" && ( -
-
- -
-
- )} - {subModule === "mechanics" && ( -
-
- -
-
- )} - {subModule === "analysis" && ( -
-
- -
-
- )} - - )} - - {/* realtime visualization */} - {activeModule === "visualization" && } - - )} -
- ); +
+ ); }; export default SideBarRight; diff --git a/app/src/components/layout/sidebarRight/properties/FloorProperties.tsx b/app/src/components/layout/sidebarRight/properties/FloorProperties.tsx new file mode 100644 index 0000000..41e8d41 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/FloorProperties.tsx @@ -0,0 +1,171 @@ +import { useEffect, useState } from "react"; +import InputToggle from "../../../ui/inputs/InputToggle"; +import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; + +import defaultTexture from '../../../../assets/textures/floor/white.png'; +import flootTexture1 from '../../../../assets/textures/floor/factory wall texture.jpg'; + +import { useBuilderStore } from "../../../../store/builder/useBuilderStore"; + +type Material = { + texture: string; + textureId: string; + textureName: string; +}; + +const materials = [ + { texture: defaultTexture, textureId: "Default Material", textureName: "Default Material" }, + { texture: flootTexture1, textureId: "Material 1", textureName: "Grunge Concrete Wall" } +]; + +const FloorProperties = () => { + const { floorDepth, isBeveled, topMaterial, sideMaterial, bevelStrength, setFloorDepth, setIsBeveled, setBevelStrength, setFloorMaterial } = useBuilderStore(); + + const [activeSurface, setActiveSurface] = useState<"top" | "side">("top"); + + const [selectedMaterials, setSelectedMaterials] = useState<{ top: Material | null; side: Material | null; }>({ top: null, side: null, }); + + useEffect(() => { + setSelectedMaterials({ + top: materials.find((mat) => mat.textureId === topMaterial) || null, + side: materials.find((mat) => mat.textureId === sideMaterial) || null, + }); + }, []); + + const handleDepthChange = (val: string) => { + setFloorDepth(parseFloat(val)); + }; + + const handleIsBevelChange = (val: boolean) => { + setIsBeveled(val); + }; + + const handleBevelChange = (val: string) => { + setBevelStrength(parseFloat(val)); + }; + + const handleSelectMaterial = (material: Material) => { + setSelectedMaterials((prev) => ({ + ...prev, + [activeSurface]: material, + })); + setFloorMaterial(material.textureId, activeSurface); + }; + + return ( +
+
+
Floor
+
+ + handleIsBevelChange(!isBeveled)} + /> + +
+
+ +
+
+
Materials
+
+ +
+
+ + + +
+ +
+ {selectedMaterials[activeSurface] && ( + {selectedMaterials[activeSurface]!.textureName} + )} +
+
+ +
+ {materials.length === 0 ? ( +
No materials added yet.
+ ) : ( +
+ {materials.map((material, index) => { + const isSelected = selectedMaterials[activeSurface]?.texture === material.texture; + + return ( + + ); + })} +
+ )} +
+
+
+ ); +}; + +export default FloorProperties; diff --git a/app/src/components/layout/sidebarRight/properties/SelectedFloorProperties.tsx b/app/src/components/layout/sidebarRight/properties/SelectedFloorProperties.tsx new file mode 100644 index 0000000..8df2a88 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/SelectedFloorProperties.tsx @@ -0,0 +1,250 @@ +import { useEffect, useState } from "react"; +import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; +import InputToggle from "../../../ui/inputs/InputToggle"; + +import defaultTexture from '../../../../assets/textures/floor/white.png'; +import floorTexture1 from '../../../../assets/textures/floor/factory wall texture.jpg'; + +import { useBuilderStore } from "../../../../store/builder/useBuilderStore"; +import { useSceneContext } from "../../../../modules/scene/sceneContext"; +import { useVersionContext } from "../../../../modules/builder/version/versionContext"; +import { useParams } from "react-router-dom"; +import { getUserData } from "../../../../functions/getUserData"; +import { useSocketStore } from "../../../../store/builder/store"; + +// import { upsertFloorApi } from "../../../../services/factoryBuilder/floor/upsertFloorApi"; + +const SelectedFloorProperties = () => { + const [depth, setDepth] = useState(""); + const [isBeveled, setIsBeveled] = useState(false); + const [bevelStrength, setBevelStrength] = useState(""); + const { selectedFloor } = useBuilderStore(); + const { floorStore } = useSceneContext(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { socket } = useSocketStore(); + const { userId, organization } = getUserData(); + const { projectId } = useParams(); + const { getFloorById, updateFloor } = floorStore(); + + const [activeSurface, setActiveSurface] = useState<"top" | "side">("top"); + + const materials = [ + { texture: defaultTexture, textureId: "Default Material", textureName: "Default Material" }, + { texture: floorTexture1, textureId: "Material 1", textureName: "Grunge Concrete" } + ]; + + const floor = selectedFloor ? getFloorById(selectedFloor.userData.floorUuid) : null; + + useEffect(() => { + if (floor) { + setDepth(floor.floorDepth.toString()); + setIsBeveled(floor.isBeveled); + setBevelStrength(floor.bevelStrength.toString()); + } + }, [floor]); + + const handleDepthChange = (val: string) => { + setDepth(val); + const parsed = parseFloat(val); + if (!isNaN(parsed) && floor) { + const updatedFloor = updateFloor(floor.floorUuid, { floorDepth: parsed }); + if (projectId) { + + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', floor); + + // SOCKET + + const data = { + floorData: updatedFloor, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + } + } + }; + + const handleBevelChange = (val: string) => { + setBevelStrength(val); + const parsed = parseFloat(val); + if (!isNaN(parsed) && floor) { + const updatedFloor = updateFloor(floor.floorUuid, { bevelStrength: parsed }); + if (projectId) { + + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', floor); + + // SOCKET + + const data = { + floorData: updatedFloor, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + } + } + }; + + const handleIsBeveledToggle = () => { + setIsBeveled(!isBeveled); + if (!floor) return; + const updatedFloor = updateFloor(floor.floorUuid, { isBeveled: !floor.isBeveled }); + if (projectId) { + + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', floor); + + // SOCKET + + const data = { + floorData: updatedFloor, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + } + + }; + + const handleSelectMaterial = (material: { textureId: string; textureName: string }) => { + if (!floor) return; + const key = activeSurface === "top" ? "topMaterial" : "sideMaterial"; + const updatedFloor = updateFloor(floor.floorUuid, { [key]: material.textureId }); + if (projectId) { + + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', floor); + + // SOCKET + + const data = { + floorData: updatedFloor, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + } + + }; + + if (!floor) return null; + + const selectedMaterials = { + top: materials.find((m) => m.textureId === floor.topMaterial) ?? materials[0], + side: materials.find((m) => m.textureId === floor.sideMaterial) ?? materials[0], + }; + + return ( +
+
+
Floor
+
+ + + +
+
+ +
+
+
Materials
+
+ +
+
+ {(["top", "side"] as const).map((surface) => ( + + ))} +
+ +
+ {selectedMaterials[activeSurface].textureName} +
+
+ +
+
+ {materials.map((material, index) => { + const isSelected = selectedMaterials[activeSurface].textureId === material.textureId; + return ( + + ); + })} +
+
+
+
+ ); +}; + +export default SelectedFloorProperties; diff --git a/app/src/components/layout/sidebarRight/properties/SelectedWallProperties.tsx b/app/src/components/layout/sidebarRight/properties/SelectedWallProperties.tsx index b647145..794ede0 100644 --- a/app/src/components/layout/sidebarRight/properties/SelectedWallProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/SelectedWallProperties.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; import defaultTexture from '../../../../assets/textures/floor/wall-tex.png'; @@ -6,10 +6,23 @@ import wallTexture1 from '../../../../assets/textures/floor/factory wall texture import { useBuilderStore } from "../../../../store/builder/useBuilderStore"; import { useSceneContext } from "../../../../modules/scene/sceneContext"; +import { useVersionContext } from "../../../../modules/builder/version/versionContext"; +import { useParams } from "react-router-dom"; +import { getUserData } from "../../../../functions/getUserData"; +import { useSocketStore } from "../../../../store/builder/store"; + +// import { upsertWallApi } from "../../../../services/factoryBuilder/wall/upsertWallApi"; const SelectedWallProperties = () => { + const [height, setHeight] = useState(""); + const [thickness, setThickness] = useState(""); const { selectedWall } = useBuilderStore(); const { wallStore } = useSceneContext(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { socket } = useSocketStore(); + const { userId, organization } = getUserData(); + const { projectId } = useParams(); const { getWallById, updateWall } = wallStore(); const [activeSide, setActiveSide] = useState<"side1" | "side2">("side1"); @@ -21,17 +34,62 @@ const SelectedWallProperties = () => { const wall = selectedWall ? getWallById(selectedWall.userData.wallUuid) : null; + useEffect(() => { + if (wall) { + setHeight(wall.wallHeight.toString()); + setThickness(wall.wallThickness.toString()); + } + }, [wall, selectedWall]); + const handleHeightChange = (val: string) => { + setHeight(val); const height = parseFloat(val); if (!isNaN(height) && wall) { - updateWall(wall.wallUuid, { wallHeight: height }); + const updatedWall = updateWall(wall.wallUuid, { wallHeight: height }); + if (updatedWall && projectId) { + + // API + + // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall); + + // SOCKET + + const data = { + wallData: updatedWall, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + } } }; const handleThicknessChange = (val: string) => { + setThickness(val); const thickness = parseFloat(val); if (!isNaN(thickness) && wall) { - updateWall(wall.wallUuid, { wallThickness: thickness }); + const updatedWall = updateWall(wall.wallUuid, { wallThickness: thickness }); + if (updatedWall && projectId) { + + // API + + // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall); + + // SOCKET + + const data = { + wallData: updatedWall, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + } } }; @@ -39,23 +97,32 @@ const SelectedWallProperties = () => { if (!wall) return; const updated = (activeSide === "side1" ? { insideMaterial: material.textureId } : { outsideMaterial: material.textureId }) + const updatedWall = updateWall(wall.wallUuid, updated); + if (updatedWall && projectId) { - updateWall(wall.wallUuid, updated); + // API + + // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall); + + // SOCKET + + const data = { + wallData: updatedWall, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + } }; if (!wall) return null; const selectedMaterials = { - side1: { - texture: materials.find((material) => material.textureId === wall.insideMaterial)?.texture || 'Unknown', - textureId: materials.find((material) => material.textureId === wall.insideMaterial)?.textureId || 'Unknown', - textureName: materials.find((material) => material.textureId === wall.insideMaterial)?.textureName || 'Unknown' - }, - side2: { - texture: materials.find((material) => material.textureId === wall.outsideMaterial)?.texture || 'Unknown', - textureId: materials.find((material) => material.textureId === wall.outsideMaterial)?.textureId || 'Unknown', - textureName: materials.find((material) => material.textureId === wall.outsideMaterial)?.textureName || 'Unknown' - } + side1: materials.find((m) => m.textureId === wall.insideMaterial) ?? materials[0], + side2: materials.find((m) => m.textureId === wall.outsideMaterial) ?? materials[0] }; return ( @@ -65,12 +132,18 @@ const SelectedWallProperties = () => {
diff --git a/app/src/components/layout/sidebarRight/properties/WallProperties.tsx b/app/src/components/layout/sidebarRight/properties/WallProperties.tsx index eb835f1..e158b44 100644 --- a/app/src/components/layout/sidebarRight/properties/WallProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/WallProperties.tsx @@ -10,163 +10,158 @@ import { useBuilderStore } from "../../../../store/builder/useBuilderStore"; // Define Material type type Material = { - texture: string; - textureName: string; + texture: string; + textureId: string; + textureName: string; }; -// Default and initial materials -const defaultMaterial: Material = { - texture: defaultTexture, - textureName: "Default Material", -}; - -const initialMaterial: Material = { - texture: wallTexture1, - textureName: "Grunge Concrete Wall", -}; +const materials = [ + { texture: defaultTexture, textureId: "Default Material", textureName: "Default Material" }, + { texture: wallTexture1, textureId: "Material 1", textureName: "Grunge Concrete Wall" } +]; const WallProperties = () => { - const { wallHeight, wallThickness, setWallHeight, setWallThickness } = useBuilderStore(); + const { wallHeight, wallThickness, insideMaterial, outsideMaterial, setWallHeight, setWallThickness } = useBuilderStore(); - const [activeSide, setActiveSide] = useState<"side1" | "side2">("side1"); + const [activeSide, setActiveSide] = useState<"side1" | "side2">("side1"); - const [materials, setMaterials] = useState([ - defaultMaterial, - initialMaterial, - ]); - - const [selectedMaterials, setSelectedMaterials] = useState<{ - side1: Material | null; - side2: Material | null; - }>({ - side1: null, - side2: null, - }); - - // Set default material initially for both sides - useEffect(() => { - setSelectedMaterials({ - side1: defaultMaterial, - side2: defaultMaterial, + const [selectedMaterials, setSelectedMaterials] = useState<{ + side1: Material | null; + side2: Material | null; + }>({ + side1: null, + side2: null, }); - }, []); - const handleHeightChange = (newValue: string) => { - setWallHeight(parseFloat(newValue)); - }; + useEffect(() => { + setSelectedMaterials({ + side1: materials.find((mat) => mat.textureId === outsideMaterial) || null, + side2: materials.find((mat) => mat.textureId === insideMaterial) || null, + }); + }, []); - const handleThicknessChange = (newValue: string) => { - setWallThickness(parseFloat(newValue)); - }; + const handleHeightChange = (newValue: string) => { + setWallHeight(parseFloat(newValue)); + }; - const handleSelectMaterial = (material: Material) => { - setSelectedMaterials((prev) => ({ - ...prev, - [activeSide]: material, - })); - }; + const handleThicknessChange = (newValue: string) => { + setWallThickness(parseFloat(newValue)); + }; - return ( -
-
-
Wall
-
- handleHeightChange(val)} - /> - handleThicknessChange(val)} - /> -
-
-
-
-
Materials
-
+ const handleSelectMaterial = (material: Material) => { + setSelectedMaterials((prev) => ({ + ...prev, + [activeSide]: material, + })); + }; -
-
- + return ( +
+
+
Wall
+
+ handleHeightChange(val)} + /> + handleThicknessChange(val)} + /> +
+
+
+
+
Materials
+
- -
+
+
+ -
- {selectedMaterials[activeSide] && ( - {selectedMaterials[activeSide]!.textureName} - )} -
-
- -
- {materials.length === 0 ? ( -
No materials added yet.
- ) : ( -
- {materials.map((material, index) => { - const isSelected = selectedMaterials[activeSide]?.texture === material.texture; - - return ( -
- - ); - })} -
- )} + +
+ {selectedMaterials[activeSide] && ( + {selectedMaterials[activeSide]!.textureName} + )} +
+
+ +
+ {materials.length === 0 ? ( +
No materials added yet.
+ ) : ( +
+ {materials.map((material, index) => { + const isSelected = selectedMaterials[activeSide]?.texture === material.texture; + + return ( + + ); + })} +
+ )} +
+
- - - ); + ); }; export default WallProperties; diff --git a/app/src/components/ui/list/List.tsx b/app/src/components/ui/list/List.tsx index 0d81bda..cf13a4f 100644 --- a/app/src/components/ui/list/List.tsx +++ b/app/src/components/ui/list/List.tsx @@ -17,7 +17,7 @@ import { useZones, } from "../../../store/builder/store"; import { zoneCameraUpdate } from "../../../services/visulization/zone/zoneCameraUpdation"; -import { setAssetsApi } from "../../../services/factoryBuilder/assest/floorAsset/setAssetsApi"; +import { setAssetsApi } from "../../../services/factoryBuilder/asset/floorAsset/setAssetsApi"; import { useParams } from "react-router-dom"; import { getUserData } from "../../../functions/getUserData"; import { useSceneContext } from "../../../modules/scene/sceneContext"; diff --git a/app/src/modules/builder/Decal/decalInstance.tsx b/app/src/modules/builder/Decal/decalInstance.tsx index 6ed0490..1b68f3d 100644 --- a/app/src/modules/builder/Decal/decalInstance.tsx +++ b/app/src/modules/builder/Decal/decalInstance.tsx @@ -7,8 +7,8 @@ import { useBuilderStore } from '../../../store/builder/useBuilderStore'; import defaultMaterial from '../../../assets/textures/floor/wall-tex.png'; import useModuleStore from '../../../store/useModuleStore'; -function DecalInstance({ visible = true, decal }: { visible?: boolean, decal: Decal }) { - const { setSelectedWall, selectedDecal, setSelectedDecal } = useBuilderStore(); +function DecalInstance({ visible = true, decal, zPosition = decal.decalPosition[2] }: { visible?: boolean, decal: Decal, zPosition?: number }) { + const { setSelectedWall, setSelectedFloor, selectedDecal, setSelectedDecal } = useBuilderStore(); const { togglView } = useToggleView(); const { activeModule } = useModuleStore(); const material = useLoader(THREE.TextureLoader, defaultMaterial); @@ -17,15 +17,17 @@ function DecalInstance({ visible = true, decal }: { visible?: boolean, decal: De { if (visible && !togglView && activeModule === 'builder') { if (e.object.userData.decalUuid) { + e.stopPropagation(); setSelectedDecal(e.object); setSelectedWall(null); + setSelectedFloor(null); } } }} diff --git a/app/src/modules/builder/IntialLoad/loadInitialLine.ts b/app/src/modules/builder/IntialLoad/loadInitialLine.ts deleted file mode 100644 index f8c3132..0000000 --- a/app/src/modules/builder/IntialLoad/loadInitialLine.ts +++ /dev/null @@ -1,30 +0,0 @@ -import addLineToScene from '../../builder/geomentries/lines/addLineToScene'; -import * as CONSTANTS from '../../../types/world/worldConstants'; -import * as Types from "../../../types/world/worldTypes"; - -function loadInitialLine( - floorPlanGroupLine: Types.RefGroup, - lines: Types.RefLines -): void { - - if (!floorPlanGroupLine.current) return - - ////////// Load the Lines initially if there are any ////////// - - floorPlanGroupLine.current.children = []; - lines.current.forEach((line) => { - let colour; - if (line[0][3] && line[1][3] === CONSTANTS.lineConfig.wallName) { - colour = CONSTANTS.lineConfig.wallColor; - } else if (line[0][3] && line[1][3] === CONSTANTS.lineConfig.floorName) { - colour = CONSTANTS.lineConfig.floorColor; - } else if (line[0][3] && line[1][3] === CONSTANTS.lineConfig.aisleName) { - colour = CONSTANTS.lineConfig.aisleColor; - } - if (colour) { - addLineToScene(line[0][0], line[1][0], colour, line, floorPlanGroupLine); - } - }); -} - -export default loadInitialLine; diff --git a/app/src/modules/builder/IntialLoad/loadInitialPoint.ts b/app/src/modules/builder/IntialLoad/loadInitialPoint.ts deleted file mode 100644 index 91db577..0000000 --- a/app/src/modules/builder/IntialLoad/loadInitialPoint.ts +++ /dev/null @@ -1,87 +0,0 @@ -import * as THREE from 'three'; - -import * as CONSTANTS from '../../../types/world/worldConstants'; -import * as Types from "../../../types/world/worldTypes"; - -////////// Load the Boxes initially if there are any ////////// - -function loadInitialPoint( - lines: Types.RefLines, - floorPlanGroupPoint: Types.RefGroup, - currentLayerPoint: Types.RefMeshArray, - dragPointControls: Types.RefDragControl -): void { - - if (!floorPlanGroupPoint.current) return - - floorPlanGroupPoint.current.children = []; - currentLayerPoint.current = []; - lines.current.forEach((line) => { - const colour = getPointColor(line[0][3]); - line.forEach((pointData) => { - const [point, id] = pointData; - - /////////// Check if a box with this id already exists ////////// - - const existingBox = floorPlanGroupPoint.current?.getObjectByProperty('uuid', id); - if (existingBox) { - return; - } - - const geometry = new THREE.BoxGeometry(...CONSTANTS.pointConfig.boxScale); - const material = new THREE.ShaderMaterial({ - uniforms: { - 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: ` - varying vec2 vUv; - - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); - } - `, - fragmentShader: ` - varying vec2 vUv; - uniform vec3 uOuterColor; - uniform vec3 uInnerColor; - - void main() { - // Define the size of the white square as a proportion of the face - float borderThickness = 0.2; // Adjust this value for border thickness - if (vUv.x > borderThickness && vUv.x < 1.0 - borderThickness && - vUv.y > borderThickness && vUv.y < 1.0 - borderThickness) { - gl_FragColor = vec4(uInnerColor, 1.0); // White inner square - } else { - gl_FragColor = vec4(uOuterColor, 1.0); // Blue border - } - } - `, - }); - const box = new THREE.Mesh(geometry, material); - box.name = "point"; - box.uuid = id; - box.userData = { type: line[0][3], color: colour }; - box.position.set(point.x, point.y, point.z); - currentLayerPoint.current.push(box); - - floorPlanGroupPoint.current?.add(box); - }); - }); - - function getPointColor(lineType: string | undefined): string { - switch (lineType) { - case CONSTANTS.lineConfig.wallName: return CONSTANTS.pointConfig.wallOuterColor; - case CONSTANTS.lineConfig.floorName: return CONSTANTS.pointConfig.floorOuterColor; - case CONSTANTS.lineConfig.aisleName: return CONSTANTS.pointConfig.aisleOuterColor; - default: return CONSTANTS.pointConfig.defaultOuterColor; - } - } - - if (dragPointControls.current) { - dragPointControls.current!.objects = currentLayerPoint.current; - } -} - -export default loadInitialPoint; diff --git a/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts b/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts index ba82510..80fdcb9 100644 --- a/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts @@ -1,110 +1,110 @@ -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"; -import { retrieveGLTF, storeGLTF } from "../../../utils/indexDB/idbUtils"; -import { getUserData } from "../../../functions/getUserData"; +// 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/asset/wallAsset/getWallItemsApi"; +// import { retrieveGLTF, storeGLTF } from "../../../utils/indexDB/idbUtils"; +// import { getUserData } from "../../../functions/getUserData"; -async function loadInitialWallItems( - setWallItems: Types.setWallItemSetState, - projectId?: string, - versionId?: string -): Promise { - if (!projectId || !versionId) return; - try { - const { organization, email } = getUserData(); +// async function loadInitialWallItems( +// setWallItems: Types.setWallItemSetState, +// projectId?: string, +// versionId?: string +// ): Promise { +// if (!projectId || !versionId) return; +// try { +// const { organization, email } = getUserData(); - if (!email) { - console.error("No email found in localStorage"); - } +// if (!email) { +// console.error("No email found in localStorage"); +// } - const items = await getWallItems(organization, projectId, versionId); +// const items = await getWallItems(organization, projectId, versionId); - let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; +// let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - if (!items || items.length === 0) { - localStorage.removeItem("WallItems"); - return; - } +// if (!items || items.length === 0) { +// localStorage.removeItem("WallItems"); +// return; +// } - localStorage.setItem("WallItems", JSON.stringify(items)); +// 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 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.assetId!); - if (cachedModel) { - return processModel(cachedModel, item); - } +// const loadedWallItems = await Promise.all( +// items.map(async (item: Types.WallItem) => { +// // Check THREE.js cache first +// const cachedModel = THREE.Cache.get(item.assetId!); +// if (cachedModel) { +// return processModel(cachedModel, item); +// } - // Check IndexedDB cache - const cachedModelBlob = await retrieveGLTF(item.assetId!); - if (cachedModelBlob) { - const blobUrl = URL.createObjectURL(cachedModelBlob); - return new Promise((resolve) => { - loader.load(blobUrl, (gltf) => { - URL.revokeObjectURL(blobUrl); - THREE.Cache.add(item.assetId!, gltf); - resolve(processModel(gltf, item)); - }); - }); - } +// // Check IndexedDB cache +// const cachedModelBlob = await retrieveGLTF(item.assetId!); +// if (cachedModelBlob) { +// const blobUrl = URL.createObjectURL(cachedModelBlob); +// return new Promise((resolve) => { +// loader.load(blobUrl, (gltf) => { +// URL.revokeObjectURL(blobUrl); +// THREE.Cache.add(item.assetId!, gltf); +// resolve(processModel(gltf, item)); +// }); +// }); +// } - // Load from original URL if not cached - const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.assetId!}`; - return new Promise((resolve) => { - loader.load(modelUrl, async (gltf) => { - try { - // Cache the model - const modelBlob = await fetch(modelUrl).then((res) => res.blob()); - await storeGLTF(item.assetId!, modelBlob); - THREE.Cache.add(item.assetId!, gltf); - resolve(processModel(gltf, item)); - } catch (error) { - console.error("Failed to cache model:", error); - resolve(processModel(gltf, item)); - } - }); - }); - }) - ); +// // Load from original URL if not cached +// const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.assetId!}`; +// return new Promise((resolve) => { +// loader.load(modelUrl, async (gltf) => { +// try { +// // Cache the model +// const modelBlob = await fetch(modelUrl).then((res) => res.blob()); +// await storeGLTF(item.assetId!, modelBlob); +// THREE.Cache.add(item.assetId!, gltf); +// resolve(processModel(gltf, item)); +// } catch (error) { +// console.error("Failed to cache model:", error); +// resolve(processModel(gltf, item)); +// } +// }); +// }); +// }) +// ); - setWallItems(loadedWallItems); - } catch (error) { - console.error("Failed to load wall items:", error); - } -} +// setWallItems(loadedWallItems); +// } catch (error) { +// console.error("Failed to load wall items:", error); +// } +// } -function processModel(gltf: GLTF, item: Types.WallItem): Types.WallItem { - const model = gltf.scene.clone(); - model.uuid = item.modelUuid!; +// 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; - } - }); +// 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, - assetId: item.assetId, - scale: item.scale, - csgscale: item.csgscale, - csgposition: item.csgposition, - position: item.position, - quaternion: item.quaternion, - }; -} +// return { +// type: item.type, +// model: model, +// modelName: item.modelName, +// assetId: item.assetId, +// scale: item.scale, +// csgscale: item.csgscale, +// csgposition: item.csgposition, +// position: item.position, +// quaternion: item.quaternion, +// }; +// } -export default loadInitialWallItems; +// export default loadInitialWallItems; diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arcAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arcAisle.tsx index d4c27b6..bc09b68 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arcAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arcAisle.tsx @@ -78,7 +78,7 @@ function ArcAisle({ aisle }: { readonly aisle: Aisle }) { position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]} rotation={[Math.PI / 2, 0, 0]} userData={aisle} - onClick={handleClick} + onDoubleClick={handleClick} onPointerMissed={() => { setSelectedAisle(null); }} diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowAisle.tsx index 941fb7f..49bf666 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowAisle.tsx @@ -65,7 +65,7 @@ function ArrowAisle({ aisle }: { readonly aisle: Aisle }) { position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]} rotation={[Math.PI / 2, 0, 0]} userData={aisle} - onClick={handleClick} + onDoubleClick={handleClick} onPointerMissed={() => { setSelectedAisle(null); }} diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowsAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowsAisle.tsx index 5dc6ee7..8bc6fd5 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowsAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/arrowsAisle.tsx @@ -68,7 +68,7 @@ function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) { position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]} rotation={[Math.PI / 2, 0, 0]} userData={aisle} - onClick={handleClick} + onDoubleClick={handleClick} onPointerMissed={() => { setSelectedAisle(null); }} diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/circleAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/circleAisle.tsx index 6d838de..af852e8 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/circleAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/circleAisle.tsx @@ -53,7 +53,7 @@ function CircleAisle({ aisle }: { readonly aisle: Aisle }) { position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]} rotation={[Math.PI / 2, 0, 0]} userData={aisle} - onClick={handleClick} + onDoubleClick={handleClick} onPointerMissed={() => { setSelectedAisle(null); }} diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dashedAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dashedAisle.tsx index 0c3eb7f..bbdad90 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dashedAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dashedAisle.tsx @@ -66,7 +66,7 @@ function DashedAisle({ aisle }: { readonly aisle: Aisle }) { position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]} rotation={[Math.PI / 2, 0, 0]} userData={aisle} - onClick={handleClick} + onDoubleClick={handleClick} onPointerMissed={() => { setSelectedAisle(null); }} diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dottedAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dottedAisle.tsx index f701fba..bf17d08 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dottedAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/dottedAisle.tsx @@ -53,7 +53,7 @@ function DottedAisle({ aisle }: { readonly aisle: Aisle }) { position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]} rotation={[Math.PI / 2, 0, 0]} userData={aisle} - onClick={handleClick} + onDoubleClick={handleClick} onPointerMissed={() => { setSelectedAisle(null); }} diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/junctionAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/junctionAisle.tsx index 23ea6e6..a3f8bdf 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/junctionAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/junctionAisle.tsx @@ -100,7 +100,7 @@ function JunctionAisle({ aisle }: { readonly aisle: Aisle }) { position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]} rotation={[Math.PI / 2, 0, 0]} userData={aisle} - onClick={handleClick} + onDoubleClick={handleClick} onPointerMissed={() => { setSelectedAisle(null); }} diff --git a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/solidAisle.tsx b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/solidAisle.tsx index ed78112..e9321b0 100644 --- a/app/src/modules/builder/aisle/Instances/instance/aisleTypes/solidAisle.tsx +++ b/app/src/modules/builder/aisle/Instances/instance/aisleTypes/solidAisle.tsx @@ -50,7 +50,7 @@ function SolidAisle({ aisle }: { readonly aisle: Aisle }) { position={[0, (aisle.points[0].layer - 1) * Constants.wallConfig.height + 0.01, 0]} rotation={[Math.PI / 2, 0, 0]} userData={aisle} - onClick={handleClick} + onDoubleClick={handleClick} onPointerMissed={() => { setSelectedAisle(null); }} diff --git a/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx b/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx index e0805e9..31ed1f6 100644 --- a/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx +++ b/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx @@ -2,13 +2,13 @@ import * as THREE from 'three' import { useEffect, useMemo, useRef, useState } from 'react' import { useThree } from '@react-three/fiber'; import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store'; -import ReferenceAisle from './referenceAisle'; import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; -import ReferencePoint from '../../point/reference/referencePoint'; -import { createAisleApi } from '../../../../services/factoryBuilder/aisle/createAisleApi'; +import { upsertAisleApi } from '../../../../services/factoryBuilder/aisle/upsertAisleApi'; import { useParams } from 'react-router-dom'; import { useVersionContext } from '../../version/versionContext'; import { useSceneContext } from '../../../scene/sceneContext'; +import ReferenceAisle from './referenceAisle'; +import ReferencePoint from '../../point/reference/referencePoint'; function AisleCreator() { const { scene, camera, raycaster, gl, pointer } = useThree(); @@ -74,6 +74,8 @@ function AisleCreator() { newPoint.layer = snappedPoint.layer; } + if (snappedPoint && snappedPoint.pointUuid === tempPoints[0]?.pointUuid) { return } + if (snappedPosition && !snappedPoint) { newPoint.position = snappedPosition; } @@ -104,7 +106,7 @@ function AisleCreator() { }; addAisle(aisle); if (projectId) { - createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') } setTempPoints([newPoint]); } @@ -127,7 +129,7 @@ function AisleCreator() { }; addAisle(aisle); if (projectId) { - createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') } setTempPoints([newPoint]); } @@ -149,7 +151,7 @@ function AisleCreator() { }; addAisle(aisle); if (projectId) { - createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') } setTempPoints([newPoint]); } @@ -170,7 +172,7 @@ function AisleCreator() { }; addAisle(aisle); if (projectId) { - createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') } setTempPoints([newPoint]); } @@ -193,7 +195,7 @@ function AisleCreator() { }; addAisle(aisle); if (projectId) { - createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') } setTempPoints([newPoint]); } @@ -215,7 +217,7 @@ function AisleCreator() { }; addAisle(aisle); if (projectId) { - createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') } setTempPoints([newPoint]); } @@ -236,7 +238,7 @@ function AisleCreator() { }; addAisle(aisle); if (projectId) { - createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') } setTempPoints([newPoint]); } @@ -258,7 +260,7 @@ function AisleCreator() { }; addAisle(aisle); if (projectId) { - createAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') + upsertAisleApi(aisle.aisleUuid, aisle.points, aisle.type, projectId, selectedVersion?.versionId || '') } setTempPoints([newPoint]); } diff --git a/app/src/modules/builder/aisle/aisleCreator/referenceAisle.tsx b/app/src/modules/builder/aisle/aisleCreator/referenceAisle.tsx index 10bf6fa..6ce66fe 100644 --- a/app/src/modules/builder/aisle/aisleCreator/referenceAisle.tsx +++ b/app/src/modules/builder/aisle/aisleCreator/referenceAisle.tsx @@ -177,7 +177,7 @@ function ReferenceAisle({ tempPoints }: Readonly) { useEffect(() => { setTempAisle(null); - }, [toolMode, toggleView, tempPoints.length, aisleType, aisleWidth, aisleColor]); + }, [toolMode, toggleView, tempPoints.length, aisleType, aisleWidth, aisleColor, activeLayer]); if (!tempAisle) return null; diff --git a/app/src/modules/builder/asset/assetsGroup.tsx b/app/src/modules/builder/asset/assetsGroup.tsx index b597ab3..13fb414 100644 --- a/app/src/modules/builder/asset/assetsGroup.tsx +++ b/app/src/modules/builder/asset/assetsGroup.tsx @@ -1,6 +1,6 @@ import * as THREE from "three" import { useEffect } from 'react' -import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; +import { getFloorAssets } from '../../../services/factoryBuilder/asset/floorAsset/getFloorItemsApi'; import { useLoadingProgress, useRenameModeStore, useSelectedFloorItem, useSelectedItem, useSocketStore } from '../../../store/builder/store'; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; @@ -23,10 +23,10 @@ const gltfLoaderWorker = new Worker( ) ); -function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, readonly plane: RefMesh }) { +function AssetsGroup({ plane }: { readonly plane: RefMesh }) { const { activeModule } = useModuleStore(); const { socket } = useSocketStore(); - const { controls, gl, pointer, camera, raycaster } = useThree(); + const { controls, gl, pointer, camera, raycaster, scene } = useThree(); const { setLoadingProgress } = useLoadingProgress(); const { assetStore, eventStore } = useSceneContext(); const { selectedVersionStore } = useVersionContext(); @@ -269,7 +269,7 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea useEffect(() => { const canvasElement = gl.domElement; - const onDrop = (event: any) => { + const onDrop = (event: DragEvent) => { if (!event.dataTransfer?.files[0]) return; if (selectedItem.id !== "" && event.dataTransfer?.files[0] && selectedItem.category !== 'Fenestration') { @@ -277,7 +277,7 @@ function AssetsGroup({ floorGroup, plane }: { readonly floorGroup: RefGroup, rea 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, selectedVersion, projectId, userId); + addAssetModel(scene, raycaster, camera, pointer, socket, selectedItem, setSelectedItem, addEvent, addAsset, plane, selectedVersion, projectId, userId); } }; diff --git a/app/src/modules/builder/asset/functions/addAssetModel.ts b/app/src/modules/builder/asset/functions/addAssetModel.ts index 2eaa744..7c85a49 100644 --- a/app/src/modules/builder/asset/functions/addAssetModel.ts +++ b/app/src/modules/builder/asset/functions/addAssetModel.ts @@ -3,496 +3,445 @@ 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 { setAssetsApi } from '../../../../services/factoryBuilder/assest/floorAsset/setAssetsApi'; +// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi'; import { Socket } from "socket.io-client"; import * as CONSTANTS from "../../../../types/world/worldConstants"; import PointsCalculator from "../../../simulation/events/points/functions/pointsCalculator"; import { getUserData } from "../../../../functions/getUserData"; 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, - selectedVersion?: Version | null, - projectId?: string, - userId?: string + scene: THREE.Scene, + raycaster: THREE.Raycaster, + camera: THREE.Camera, + pointer: THREE.Vector2, + socket: Socket, + selectedItem: any, + setSelectedItem: any, + addEvent: (event: EventsSchema) => void, + addAsset: (asset: Asset) => void, + plane: Types.RefMesh, + selectedVersion?: Version | null, + projectId?: string, + userId?: string ): Promise { - ////////// Load Floor GLtf's and set the positions, rotation, type etc. in state and store in localstorage ////////// + ////////// 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}`; + let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - try { - const loader = new GLTFLoader(); - const dracoLoader = new DRACOLoader(); + 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); + 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") - ); + raycaster.setFromCamera(pointer, camera); + const wallFloorsGroup = scene.getObjectByName("Walls-Floors-Group") as Types.Group | null; + const floorsGroup = scene.getObjectByName("Floors-Group") as Types.Group | null; + const floorChildren = floorsGroup?.children ?? []; + const wallFloorChildren = wallFloorsGroup?.children ?? []; + const floorIntersections = raycaster.intersectObjects([...floorChildren, ...wallFloorChildren], true); + const intersectedFloor = floorIntersections.find((intersect) => intersect.object.name.includes("Floor")); - const planeIntersections = raycaster.intersectObject(plane.current!, true); - const intersectedPlane = planeIntersections[0]; + const planeIntersections = raycaster.intersectObject(plane.current!, true); + const intersectedPlane = planeIntersections[0]; - let intersectPoint: THREE.Vector3 | null = null; + 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, - selectedVersion?.versionId || '', - projectId, - userId - ); - 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, - selectedVersion?.versionId || '', - projectId, - userId - ); - }); - } 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, - selectedVersion?.versionId || '', - projectId, - userId - ); + 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); + if (intersectedFloor.distance < intersectedPlane.distance) { + if (intersectedFloor.object.userData.floorUuid) { + intersectPoint = new THREE.Vector3(intersectedFloor.point.x, intersectedFloor.object.userData.floorDepth, intersectedFloor.point.z); + } else { + intersectPoint = new THREE.Vector3(intersectedFloor.point.x, 0, intersectedFloor.point.z); + } + } else { + intersectPoint = 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, selectedVersion?.versionId || '', projectId, userId); + 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, selectedVersion?.versionId || '', projectId, userId); + }); + } 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, selectedVersion?.versionId || '', projectId, userId); + } + ); + } + } + } + } catch (error) { + echo.error("Failed to add asset"); + } finally { + setSelectedItem({}); } - } 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, - versionId: string, - projectId?: string, - userId?: string + gltf: any, + intersectPoint: THREE.Vector3, + selectedItem: any, + addEvent: (event: EventsSchema) => void, + addAsset: (asset: Asset) => void, + socket: Socket, + versionId: string, + projectId?: string, + userId?: string ) { - const { organization } = getUserData(); - 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, - }; - - - // API - - // await setAssetsApi( - // organization, - // newFloorItem.modelUuid, - // newFloorItem.modelName, - // newFloorItem.assetId, - // 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, + const { organization } = getUserData(); + 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); - 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; + model.traverse((child: any) => { + if (child) { + child.castShadow = true; + child.receiveShadow = true; } - } - 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: [], + }); + + 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, + }; + + + // API + + // await setAssetsApi( + // organization, + // newFloorItem.modelUuid, + // newFloorItem.modelName, + // newFloorItem.assetId, + // 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, + assetId: newFloorItem.assetId, + position: newFloorItem.position, + rotation: { + x: model.rotation.x, + y: model.rotation.y, + z: model.rotation.z, }, - ], - }, - }; - 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, - }; + isLocked: false, + isVisible: true, + socketId: socket.id, + eventData: eventData, + versionId: versionId, + projectId: projectId, + userId: userId, + }; + + socket.emit("v1:model-asset:add", completeData); + + const asset: Asset = { + modelUuid: completeData.modelUuid, + modelName: completeData.modelName, + assetId: completeData.assetId, + 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, + assetId: 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, + versionId: versionId, + projectId: projectId, + userId: userId, + }; + + socket.emit("v1:model-asset:add", data); + + const asset = { + modelUuid: data.modelUuid, + modelName: data.modelName, + assetId: data.assetId, + 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); } - - const completeData = { - organization, - modelUuid: newFloorItem.modelUuid, - modelName: newFloorItem.modelName, - assetId: 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, - versionId: versionId, - projectId: projectId, - userId: userId, - }; - - socket.emit("v1:model-asset:add", completeData); - - const asset: Asset = { - modelUuid: completeData.modelUuid, - modelName: completeData.modelName, - assetId: completeData.assetId, - 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, - assetId: 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, - versionId: versionId, - projectId: projectId, - userId: userId, - }; - - socket.emit("v1:model-asset:add", data); - - const asset = { - modelUuid: data.modelUuid, - modelName: data.modelName, - assetId: data.assetId, - 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/models/model/model.tsx b/app/src/modules/builder/asset/models/model/model.tsx index 4d10c28..75a8477 100644 --- a/app/src/modules/builder/asset/models/model/model.tsx +++ b/app/src/modules/builder/asset/models/model/model.tsx @@ -4,7 +4,7 @@ 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, useToolMode } from '../../../../../store/builder/store'; +import { useActiveTool, useDeletableFloorItem, useLimitDistance, useRenderDistance, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store'; import { AssetBoundingBox } from '../../functions/assetBoundingBox'; import { CameraControls, Html } from '@react-three/drei'; import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore'; @@ -34,6 +34,7 @@ function Model({ asset }: { readonly asset: Asset }) { const { socket } = useSocketStore(); const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem(); const { setSelectedFloorItem } = useSelectedFloorItem(); + const { limitDistance } = useLimitDistance(); const { renderDistance } = useRenderDistance(); const [isRendered, setIsRendered] = useState(false); const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; @@ -162,10 +163,16 @@ function Model({ asset }: { readonly asset: Asset }) { 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); + if (limitDistance) { + if (!isRendered && assetPosition.distanceTo(camera.position) <= renderDistance) { + setIsRendered(true); + } else if (isRendered && assetPosition.distanceTo(camera.position) > renderDistance) { + setIsRendered(false); + } + } else { + if (!isRendered) { + setIsRendered(true); + } } }) diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx index b977781..ab9434b 100644 --- a/app/src/modules/builder/builder.tsx +++ b/app/src/modules/builder/builder.tsx @@ -1,139 +1,68 @@ ////////// Three and React Three Fiber Imports ////////// import * as THREE from "three"; -import { useEffect, useRef, useState } from "react"; -import { useThree, useFrame } from "@react-three/fiber"; - -////////// Component Imports ////////// - -import DistanceText from "./geomentries/lines/distanceText/distanceText"; -import ReferenceDistanceText from "./geomentries/lines/distanceText/referenceDistanceText"; +import { useEffect, useRef } from "react"; +import { useFrame, useThree } from "@react-three/fiber"; +import { Geometry } from "@react-three/csg"; ////////// Zustand State Imports ////////// import { useToggleView, - useActiveLayer, useWallVisibility, useRoofVisibility, useShadows, - useUpdateScene, - useWalls, useToolMode, - useRefTextUpdate, useRenderDistance, useLimitDistance, } from "../../store/builder/store"; ////////// 3D Function Imports ////////// -import loadWalls from "./geomentries/walls/loadWalls"; - import * as Types from "../../types/world/worldTypes"; import SocketResponses from "../collaboration/socket/socketResponses.dev"; -import FloorPlanGroup from "./groups/floorPlanGroup"; -import FloorGroup from "./groups/floorGroup"; -import Draw from "./functions/draw"; -import WallsAndWallItems from "./groups/wallsAndWallItems"; import Ground from "../scene/environment/ground"; import { findEnvironment } from "../../services/factoryBuilder/environment/findEnvironment"; -import Layer2DVisibility from "./geomentries/layers/layer2DVisibility"; -import ZoneGroup from "./groups/zoneGroup"; + 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 "./asset/assetsGroup"; -import { Bvh } from "@react-three/drei"; import DxfFile from "./dfx/LoadBlueprint"; -import { useParams } from "react-router-dom"; import AislesGroup from "./aisle/aislesGroup"; import WallGroup from "./wall/wallGroup"; +import FloorGroup from "./floor/floorGroup"; +import ZoneGroup from "./zone/zoneGroup"; + +import { useParams } from "react-router-dom"; import { useBuilderStore } from "../../store/builder/useBuilderStore"; import { getUserData } from "../../functions/getUserData"; +import WallAssetGroup from "./wallAsset/wallAssetGroup"; export default function Builder() { - const state = useThree(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements. - const csg = useRef(); // Reference for CSG object, used for 3D modeling. - const CSGGroup = useRef() as Types.RefMesh; // Reference to a group of CSG objects. - const scene = useRef() as Types.RefScene; // Reference to the scene. - const dragPointControls = useRef() as Types.RefDragControl; // Reference for drag point controls, an array for drag control. + const state = useThree(); + const plane = useRef(null); + const csgRef = useRef(null); - // Assigning the scene and camera from the Three.js state to the references. - - const plane = useRef(null); // Reference for a plane object for raycaster reference. - const grid = useRef() as any; // Reference for a grid object for raycaster reference. - const snappedPoint = useRef() as Types.RefVector3; // Reference for storing a snapped point at the (end = isSnapped) and (start = ispreSnapped) of the line. - const isSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (end). - const anglesnappedPoint = useRef() as Types.RefVector3; // Reference for storing an angle-snapped point when the line is in 90 degree etc... - const isAngleSnapped = useRef(false) as Types.RefBoolean; // Boolean to indicate if angle snapping is active. - const isSnappedUUID = useRef() as Types.RefString; // UUID reference to identify the snapped point. - const ispreSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (start). - const Tube = useRef() as Types.RefTubeGeometry; // Reference for tubes used for reference line creation and updation. - const line = useRef([]) as Types.RefLine; // Reference for line which stores the current line that is being drawn. - const lines = useRef([]) as Types.RefLines; // Reference for lines which stores all the lines that are ever drawn. - const onlyFloorline = useRef([]); // Reference for floor lines which does not have walls or roof and have only floor used to store the current line that is being drawn. - const onlyFloorlines = useRef([]); // Reference for all the floor lines that are ever drawn. - const ReferenceLineMesh = useRef() as Types.RefMesh; // Reference for storing the mesh of the reference line for moving it during draw. - const LineCreated = useRef(false) as Types.RefBoolean; // Boolean to track whether the reference line is created or not. - const referencePole = useRef() as Types.RefMesh; // Reference for a pole that is used as the reference for the user to show where it is placed. - const floorGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the roofs and the floors. - const floorPlanGroup = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines group and the points group. - const floorPlanGroupLine = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines that are drawn. - const floorPlanGroupPoint = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the points that are created. - const 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 hoveredDeletableWallItem = useRef() as Types.RefMesh; // Reference for the currently hovered wall item that can be deleted. - const hoveredDeletablePillar = useRef() as Types.RefMesh; // Reference for the currently hovered pillar that can be deleted. - const currentWallItem = useRef() as Types.RefMesh; // Reference for the currently selected wall item that can be scaled, dragged etc... - - const cursorPosition = new THREE.Vector3(); // 3D vector for storing the cursor position. - - const [selectedItemsIndex, setSelectedItemsIndex] = useState(null); // State for tracking the index of the selected item. - const { activeLayer } = useActiveLayer(); // State that changes based on which layer the user chooses in Layers.jsx. - const { toggleView } = useToggleView(); // State for toggling between 2D and 3D. - const { toolMode, setToolMode } = useToolMode(); + const { toggleView } = useToggleView(); + const { setToolMode } = useToolMode(); const { setRoofVisibility } = useRoofVisibility(); const { setWallVisibility } = useWallVisibility(); const { setShadows } = useShadows(); const { setRenderDistance } = useRenderDistance(); const { setLimitDistance } = useLimitDistance(); - const { setUpdateScene } = useUpdateScene(); - const { setWalls } = useWalls(); - const { refTextupdate, setRefTextUpdate } = useRefTextUpdate(); const { projectId } = useParams(); const { setHoveredPoint, setHoveredLine } = useBuilderStore(); const { userId, organization } = getUserData(); - // 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); - - ////////// All Toggle's ////////// - useEffect(() => { - setRefTextUpdate((prevUpdate: number) => prevUpdate - 1); - if (toggleView) { - Layer2DVisibility( - activeLayer, - floorPlanGroup, - floorPlanGroupLine, - floorPlanGroupPoint, - currentLayerPoint, - dragPointControls - ); - } else { + if (!toggleView) { setHoveredLine(null); setHoveredPoint(null); state.gl.domElement.style.cursor = 'default'; setToolMode('cursor'); - loadWalls(lines, setWalls); - setUpdateScene(true); - line.current = []; } }, [toggleView]); @@ -152,131 +81,45 @@ export default function Builder() { fetchVisibility(); }, []); - ////////// UseFrame is Here ////////// - useFrame(() => { - if (toolMode) { - Draw( - state, - plane, - cursorPosition, - floorPlanGroupPoint, - snappedPoint, - isSnapped, - isSnappedUUID, - line, - ispreSnapped, - floorPlanGroup, - ReferenceLineMesh, - LineCreated, - setRefTextUpdate, - Tube, - anglesnappedPoint, - isAngleSnapped, - toolMode - ); + if (csgRef.current) { + csgRef.current.update(); } - }); - - ////////// Return ////////// + }) return ( <> - + - - - + - + - - - + + - + - + - + + - + - + - + - + - + - + - + - - - - - - {/* */} + ); } diff --git a/app/src/modules/builder/dfx/LoadBlueprint.tsx b/app/src/modules/builder/dfx/LoadBlueprint.tsx index 4e2425d..1a95268 100644 --- a/app/src/modules/builder/dfx/LoadBlueprint.tsx +++ b/app/src/modules/builder/dfx/LoadBlueprint.tsx @@ -1,40 +1,21 @@ import { useEffect, useRef } from 'react'; import { useDfxUpload, useSocketStore, useToggleView, useUpdateScene } from '../../../store/builder/store'; import { LineBasicMaterial, Line } from 'three'; -import loadInitialPoint from '../IntialLoad/loadInitialPoint'; -import loadInitialLine from '../IntialLoad/loadInitialLine'; import { TransformControls } from '@react-three/drei'; import { getWallPointsFromBlueprint } from './functions/getWallPointsFromBlueprint'; import * as Types from '../../../types/world/worldTypes'; -import arrayLineToObject from '../geomentries/lines/lineConvertions/arrayLineToObject'; import { useParams } from 'react-router-dom'; import { getUserData } from '../../../functions/getUserData'; import { useVersionContext } from '../version/versionContext'; -// Interface defining the props for the DxfFile component -interface DxfFileProps { - lines: Types.RefLines; // Reference to lines in the DXF file - floorPlanGroupPoint: Types.RefGroup; // Reference to floor plan points group - dragPointControls: Types.RefDragControl; // Reference to drag controls - floorPlanGroupLine: Types.RefGroup; // Reference to floor plan lines group - currentLayerPoint: Types.RefMeshArray; // Reference to current layer points -} - /** * DxfFile component handles the rendering and manipulation of DXf file data in a 3D scene. * It processes the DXF data to create points and lines representing walls and allows * transformation controls for interactive editing. */ -const DxfFile = ({ - floorPlanGroupPoint, - currentLayerPoint, - dragPointControls, - floorPlanGroupLine, - lines, -}: DxfFileProps) => { +const DxfFile = () => { // State management hooks const { dfxuploaded, dfxWallGenerate, setObjValue, objValue } = useDfxUpload(); - const { setUpdateScene } = useUpdateScene(); const { toggleView } = useToggleView(); const { socket } = useSocketStore(); const { selectedVersionStore } = useVersionContext(); @@ -50,55 +31,34 @@ const DxfFile = ({ * Loads initial points and lines from the DXF data and updates the scene. */ useEffect(() => { - if ( - dfxWallGenerate && - dragPointControls && - floorPlanGroupPoint && - currentLayerPoint && - floorPlanGroupLine - ) { + if (dfxWallGenerate) { // Store generated lines in ref - lines.current.push(...dfxWallGenerate); - dfxWallGenerate.map((line: any) => { - const lineData = arrayLineToObject(line as Types.Line); + // lines.current.push(...dfxWallGenerate); + // dfxWallGenerate.map((line: any) => { + // const lineData = arrayLineToObject(line as Types.Line); - //REST + // //REST - // setLine(organization, lineData.layer!, lineData.line!, lineData.type!); + // // setLine(organization, lineData.layer!, lineData.line!, lineData.type!); - //SOCKET + // //SOCKET - const input = { - organization, - layer: lineData.layer, - line: lineData.line, - type: lineData.type, - socketId: socket.id, - versionId: selectedVersion?.versionId || '', - projectId, - userId - } + // const input = { + // organization, + // layer: lineData.layer, + // line: lineData.line, + // type: lineData.type, + // socketId: socket.id, + // versionId: selectedVersion?.versionId || '', + // projectId, + // userId + // } - console.log('input: ', input); - socket.emit('v1:Line:create', input); + // socket.emit('v1:Line:create', input); - }) - - // Load initial points and lines from DXF data - loadInitialPoint(lines, floorPlanGroupPoint, currentLayerPoint, dragPointControls); - loadInitialLine(floorPlanGroupLine, lines); - - // Trigger scene update - setUpdateScene(true); + // }) } - }, [ - lines, - floorPlanGroupLine, - floorPlanGroupPoint, - currentLayerPoint, - dragPointControls, - dfxWallGenerate, - ]); + }, [dfxWallGenerate]); /** * Handles transformation changes for individual lines. diff --git a/app/src/modules/builder/eventDeclaration/dragControlDeclaration.ts b/app/src/modules/builder/eventDeclaration/dragControlDeclaration.ts deleted file mode 100644 index 52a4e16..0000000 --- a/app/src/modules/builder/eventDeclaration/dragControlDeclaration.ts +++ /dev/null @@ -1,100 +0,0 @@ -import * as THREE from "three"; -import { DragControls } from "three/examples/jsm/controls/DragControls"; -import * as CONSTANTS from "../../../types/world/worldConstants"; -import DragPoint from "../geomentries/points/dragPoint"; - -import * as Types from "../../../types/world/worldTypes"; -// import { updatePoint } from '../../../services/factoryBuilder/lines/updatePointApi'; -import { Socket } from "socket.io-client"; -import { getUserData } from "../../../functions/getUserData"; - -export default async function addDragControl( - dragPointControls: Types.RefDragControl, - currentLayerPoint: Types.RefMeshArray, - state: Types.ThreeState, - floorPlanGroupPoint: Types.RefGroup, - floorPlanGroupLine: Types.RefGroup, - lines: Types.RefLines, - onlyFloorlines: Types.RefOnlyFloorLines, - socket: Socket, - projectId?: string, - versionId?: string -) { - ////////// Dragging Point and also change the size to indicate during hover ////////// - - dragPointControls.current = new DragControls( - currentLayerPoint.current, - state.camera, - state.gl.domElement - ); - dragPointControls.current.enabled = false; - const { userId, organization, email } = getUserData(); - dragPointControls.current.addEventListener("drag", function (event) { - const object = event.object; - if (object.visible) { - (state.controls as any).enabled = false; - DragPoint( - event as any, - floorPlanGroupPoint, - floorPlanGroupLine, - state.scene, - lines, - onlyFloorlines - ); - } else { - (state.controls as any).enabled = true; - } - }); - - dragPointControls.current.addEventListener("dragstart", function (event) { }); - - dragPointControls.current.addEventListener("dragend", async function (event) { - if (!dragPointControls.current) return; - - //REST - - // await updatePoint( - // organization, - // { "x": event.object.position.x, "y": 0.01, "z": event.object.position.z }, - // event.object.uuid, - // ) - - //SOCKET - - const data = { - organization, - position: { - x: event.object.position.x, - y: 0.01, - z: event.object.position.z, - }, - uuid: event.object.uuid, - socketId: socket.id, - versionId, - projectId, - userId, - }; - - socket.emit("v1:Line:update", data); - - if (state.controls) { - (state.controls as any).enabled = true; - } - }); - - dragPointControls.current.addEventListener("hoveron", function (event: any) { - if ((event.object as Types.Mesh).name === "point") { - event.object.material.uniforms.uInnerColor.value.set( - event.object.userData.color - ); - } - }); - - dragPointControls.current.addEventListener("hoveroff", function (event: any) { - if ((event.object as Types.Mesh).name === "point") { - event.object.material.uniforms.uInnerColor.value.set( - new THREE.Color(CONSTANTS.pointConfig.defaultInnerColor) - ); - } - }); -} diff --git a/app/src/modules/builder/eventFunctions/handleContextMenu.ts b/app/src/modules/builder/eventFunctions/handleContextMenu.ts deleted file mode 100644 index 454f847..0000000 --- a/app/src/modules/builder/eventFunctions/handleContextMenu.ts +++ /dev/null @@ -1,8 +0,0 @@ -import * as Types from "../../../types/world/worldTypes"; - -export default function handleContextMenu( - menuVisible: Types.Boolean, - setMenuVisible: Types.BooleanState -): void { - // setMenuVisible(true) -} \ No newline at end of file diff --git a/app/src/modules/builder/eventFunctions/handleMeshDown.ts b/app/src/modules/builder/eventFunctions/handleMeshDown.ts deleted file mode 100644 index 9fb7839..0000000 --- a/app/src/modules/builder/eventFunctions/handleMeshDown.ts +++ /dev/null @@ -1,61 +0,0 @@ -import * as THREE from 'three'; - -import * as Types from "../../../types/world/worldTypes"; - -function handleMeshDown( - event: Types.MeshEvent, - currentWallItem: Types.RefMesh, - setSelectedWallItem: Types.setSelectedWallItemSetState, - setSelectedItemsIndex: Types.setSelectedItemsIndexSetState, - wallItems: Types.wallItems, - toggleView: Types.Boolean -): void { - - ////////// To select which of the Wall item and CSG is selected to be dragged ////////// - - if (!toggleView) { - if (currentWallItem.current) { - 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("black"); - } - }); - } else if (material instanceof THREE.MeshStandardMaterial) { - material.emissive = new THREE.Color("black"); - } - } - }); - currentWallItem.current = null; - setSelectedWallItem(null); - setSelectedItemsIndex(null); - } - - if (event.intersections.length > 0) { - 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); - } - } - } - - } - } -} -export default handleMeshDown; diff --git a/app/src/modules/builder/eventFunctions/handleMeshMissed.ts b/app/src/modules/builder/eventFunctions/handleMeshMissed.ts deleted file mode 100644 index f374831..0000000 --- a/app/src/modules/builder/eventFunctions/handleMeshMissed.ts +++ /dev/null @@ -1,34 +0,0 @@ -import * as THREE from 'three'; -import * as Types from "../../../types/world/worldTypes"; - -function handleMeshMissed( - currentWallItem: Types.RefMesh, - setSelectedWallItem: Types.setSelectedWallItemSetState, - setSelectedItemsIndex: Types.setSelectedItemsIndexSetState -): void { - - ////////// If an item is selected and then clicked outside other than the selected object, this runs and removes the color of the selected object and sets setSelectedWallItem and setSelectedItemsIndex as null ////////// - - if (currentWallItem.current) { - 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("black"); - } - }); - } else if (material instanceof THREE.MeshStandardMaterial) { - material.emissive = new THREE.Color("black"); - } - } - }); - currentWallItem.current = null; - setSelectedWallItem(null); - setSelectedItemsIndex(null); - } -} - -export default handleMeshMissed; diff --git a/app/src/modules/builder/floor/Instances/Instance/floor2DInstance.tsx b/app/src/modules/builder/floor/Instances/Instance/floor2DInstance.tsx new file mode 100644 index 0000000..1ba15f0 --- /dev/null +++ b/app/src/modules/builder/floor/Instances/Instance/floor2DInstance.tsx @@ -0,0 +1,50 @@ +import { useMemo } from 'react'; +import { DoubleSide, Shape, Vector2 } from 'three'; +import { Extrude } from '@react-three/drei'; +import * as Constants from '../../../../../types/world/worldConstants'; + +function Floor2DInstance({ floor }: { floor: Floor }) { + const savedTheme: string | null = localStorage.getItem("theme"); + + const shape = useMemo(() => { + const shape = new Shape(); + const points = floor.points.map(p => new Vector2(p.position[0], p.position[2])); + if (points.length < 3) return null; + shape.moveTo(points[0].x, points[0].y); + for (let i = 1; i < points.length; i++) { + shape.lineTo(points[i].x, points[i].y); + } + return shape; + }, [floor]); + + if (!shape) return null; + + return ( + + + + + + ); +} + +export default Floor2DInstance; \ No newline at end of file diff --git a/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx b/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx new file mode 100644 index 0000000..c6c8139 --- /dev/null +++ b/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx @@ -0,0 +1,108 @@ +import { useMemo } from 'react'; +import { Shape, Vector2, DoubleSide, TextureLoader, RepeatWrapping, SRGBColorSpace } from 'three'; +import { useLoader } from '@react-three/fiber'; +import { Extrude } from '@react-three/drei'; +import useModuleStore from '../../../../../store/useModuleStore'; +import { useBuilderStore } from '../../../../../store/builder/useBuilderStore'; +import { useToggleView } from '../../../../../store/builder/store'; +import * as Constants from '../../../../../types/world/worldConstants'; + +import texturePath from "../../../../../assets/textures/floor/white.png"; +import texturePathDark from "../../../../../assets/textures/floor/black.png"; +import material1 from '../../../../../assets/textures/floor/factory wall texture.jpg'; + +function FloorInstance({ floor }: { floor: Floor }) { + const { togglView } = useToggleView(); + const { activeModule } = useModuleStore(); + const { selectedFloor, setSelectedFloor, setSelectedDecal } = useBuilderStore(); + const savedTheme = localStorage.getItem('theme'); + + const materials: Record = { + "Default Material": savedTheme === "dark" ? texturePathDark : texturePath, + "Material 1": savedTheme === "dark" ? material1 : material1, + }; + + const shape = useMemo(() => { + const shape = new Shape(); + const points = floor.points.map(p => new Vector2(p.position[0], p.position[2])); + if (points.length < 3) return null; + shape.moveTo(points[0].x, points[0].y); + for (let i = 1; i < points.length; i++) { + shape.lineTo(points[i].x, points[i].y); + } + return shape; + }, [floor]); + + const textureScale = Constants.floorConfig.textureScale; + + const [topTexture, sideTexture] = useLoader( + TextureLoader, + [ + materials[floor.topMaterial] || materials['Default Material'], + materials[floor.sideMaterial] || materials['Default Material'] + ] + ); + + if (!materials[floor.topMaterial] || !materials[floor.sideMaterial]) return null; + + [topTexture, sideTexture].forEach(tex => { + tex.wrapS = tex.wrapT = RepeatWrapping; + tex.repeat.set(textureScale, textureScale); + tex.colorSpace = SRGBColorSpace; + }); + + if (!shape) return null; + + return ( + { + if (!togglView && activeModule === 'builder') { + if (e.object.userData.floorUuid) { + e.stopPropagation(); + setSelectedFloor(e.object); + setSelectedDecal(null); + } + } + }} + onPointerMissed={() => { + if (selectedFloor && selectedFloor.userData.floorUuid === floor.floorUuid) { + setSelectedFloor(null); + } + }} + > + + + + + + ); +} + +export default FloorInstance; \ No newline at end of file diff --git a/app/src/modules/builder/floor/Instances/floorInstances.tsx b/app/src/modules/builder/floor/Instances/floorInstances.tsx new file mode 100644 index 0000000..a578800 --- /dev/null +++ b/app/src/modules/builder/floor/Instances/floorInstances.tsx @@ -0,0 +1,133 @@ +import React, { useEffect, useMemo } from 'react'; +import { Vector3 } from 'three'; +import { Html } from '@react-three/drei'; +import { useSceneContext } from '../../../scene/sceneContext'; +import { useToggleView } from '../../../../store/builder/store'; +import Line from '../../line/line'; +import Point from '../../point/point'; +import FloorInstance from './Instance/floorInstance'; +import Floor2DInstance from './Instance/floor2DInstance'; + +function FloorInstances() { + const { floorStore } = useSceneContext(); + const { floors } = floorStore(); + const { toggleView } = useToggleView(); + + useEffect(() => { + // console.log('floors: ', floors); + }, [floors]) + + const allPoints = useMemo(() => { + const points: Point[] = []; + const seenUuids = new Set(); + + floors.forEach(floor => { + floor.points.forEach(point => { + if (!seenUuids.has(point.pointUuid)) { + seenUuids.add(point.pointUuid); + points.push(point); + } + }); + }); + + return points; + }, [floors]); + + const allLines = useMemo(() => { + const lines: { start: Point; end: Point; key: string }[] = []; + const seenUuids = new Set(); + + floors.forEach((floor) => { + const points = floor.points; + if (points.length < 2) return; + + for (let i = 0; i < points.length; i++) { + const current = points[i]; + const next = points[(i + 1) % points.length]; + const lineKey = `${current.pointUuid}-${next.pointUuid}`; + if (current.pointUuid !== next.pointUuid && !seenUuids.has(lineKey)) { + seenUuids.add(lineKey); + lines.push({ + start: current, + end: next, + key: lineKey + }); + } + } + }); + + return lines; + }, [floors]); + + return ( + <> + + {!toggleView && floors.length > 0 && ( + + {floors.map((floor) => ( + + ))} + + )} + + {toggleView && floors.length > 0 && ( + + {floors.map((floor) => ( + + ))} + + )} + + {toggleView && ( + <> + + {allPoints.map((point) => ( + + ))} + + + + + {allLines.map(({ start, end, key }) => ( + + ))} + + {allLines.map((line) => { + const { start, end, key } = line; + const textPosition = new Vector3().addVectors(new Vector3(...start.position), new Vector3(...end.position)).divideScalar(2); + const distance = new Vector3(...start.position).distanceTo(new Vector3(...end.position)); + + return ( + + {toggleView && + +
+ {distance.toFixed(2)} m +
+ + } +
+ ) + })} + +
+ + + )} + + ) +} + +export default FloorInstances; \ No newline at end of file diff --git a/app/src/modules/builder/floor/floorCreator/floorCreator.tsx b/app/src/modules/builder/floor/floorCreator/floorCreator.tsx new file mode 100644 index 0000000..acab425 --- /dev/null +++ b/app/src/modules/builder/floor/floorCreator/floorCreator.tsx @@ -0,0 +1,267 @@ +import * as THREE from 'three' +import { useEffect, useMemo, useRef, useState } from 'react' +import { useThree } from '@react-three/fiber'; +import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store'; +import { useSceneContext } from '../../../scene/sceneContext'; +import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; +import { useParams } from 'react-router-dom'; +import { useVersionContext } from '../../version/versionContext'; +import { getUserData } from '../../../../functions/getUserData'; +import ReferencePoint from '../../point/reference/referencePoint'; +import ReferenceFloor from './referenceFloor'; + +// import { upsertFloorApi } from '../../../../services/factoryBuilder/floor/upsertFloorApi'; + +function FloorCreator() { + 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 { floorStore } = useSceneContext(); + const { addFloor, getFloorPointById, getFloorByPoints } = floorStore(); + const drag = useRef(false); + const isLeftMouseDown = useRef(false); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { userId, organization } = getUserData(); + const { projectId } = useParams(); + + const [tempPoints, setTempPoints] = useState([]); + const [isCreating, setIsCreating] = useState(false); + const { floorDepth, isBeveled, bevelStrength, sideMaterial, topMaterial, snappedPosition, snappedPoint, setSnappedPoint, setSnappedPosition } = useBuilderStore(); + + useEffect(() => { + const canvasElement = gl.domElement; + + const onMouseDown = (evt: any) => { + if (evt.button === 0) { + isLeftMouseDown.current = true; + drag.current = false; + } + }; + + const onMouseUp = (evt: any) => { + if (evt.button === 0) { + isLeftMouseDown.current = false; + } + }; + + const onMouseMove = () => { + if (isLeftMouseDown) { + drag.current = true; + } + }; + + const onMouseClick = () => { + if (drag.current || !toggleView) return; + + raycaster.setFromCamera(pointer, camera); + const intersectionPoint = new THREE.Vector3(); + let position = raycaster.ray.intersectPlane(plane, intersectionPoint); + if (!position) return; + + const pointIntersects = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Floor-Point'); + + // const floorIntersect = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Floor-Line'); + + // if (floorIntersect && !pointIntersects) { + + // } + + const newPoint: Point = { + pointUuid: THREE.MathUtils.generateUUID(), + pointType: 'Floor', + position: [position.x, position.y, position.z], + layer: activeLayer + }; + + if (snappedPosition && snappedPoint) { + newPoint.pointUuid = snappedPoint.pointUuid; + newPoint.position = snappedPosition; + newPoint.layer = snappedPoint.layer; + } + + if (snappedPoint && snappedPoint.pointUuid === tempPoints[tempPoints.length - 1]?.pointUuid) { return } + + if (snappedPosition && !snappedPoint) { + newPoint.position = snappedPosition; + } + + if (tempPoints.length > 2 && isCreating && snappedPoint && snappedPoint.pointUuid === tempPoints[0].pointUuid) { + const floor: Floor = { + floorUuid: THREE.MathUtils.generateUUID(), + floorName: "Floor", + points: tempPoints, + topMaterial, + sideMaterial, + floorDepth, + isBeveled, + bevelStrength, + decals: [], + }; + + addFloor(floor); + if (projectId) { + + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', floor); + + // SOCKET + + const data = { + floorData: floor, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + + } + setTempPoints([]); + setIsCreating(false); + } else if (isCreating && snappedPoint && !tempPoints.some(p => p.pointUuid === snappedPoint.pointUuid)) { + setTempPoints(prev => [...prev, newPoint]); + setIsCreating(true); + } else if (pointIntersects) { + if (tempPoints.length > 2 && isCreating && pointIntersects.object.uuid === tempPoints[0].pointUuid) { + const floor: Floor = { + floorUuid: THREE.MathUtils.generateUUID(), + floorName: "Floor", + points: tempPoints, + topMaterial, + sideMaterial, + floorDepth, + isBeveled, + bevelStrength, + decals: [], + }; + + addFloor(floor); + if (projectId) { + + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', floor); + + // SOCKET + + const data = { + floorData: floor, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + + } + setTempPoints([]); + setIsCreating(false); + } else if (tempPoints.length === 0 || (tempPoints.length > 1 && !tempPoints.slice(1).some(p => p.pointUuid === pointIntersects.object.uuid))) { + tempPoints.push(pointIntersects.object.userData as Point); + setIsCreating(true); + } + } else { + setTempPoints(prev => [...prev, newPoint]); + setIsCreating(true); + } + + }; + + const onContext = (event: any) => { + event.preventDefault(); + if (isCreating) { + if (tempPoints.length >= 3) { + const floor: Floor = { + floorUuid: THREE.MathUtils.generateUUID(), + floorName: "Floor", + points: tempPoints, + topMaterial, + sideMaterial, + floorDepth, + isBeveled, + bevelStrength, + decals: [], + }; + + addFloor(floor); + if (projectId) { + + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', floor); + + // SOCKET + + const data = { + floorData: floor, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + + } + } + setTempPoints([]); + setIsCreating(false); + } + }; + + if (toolMode === "Floor" && toggleView) { + if (tempPoints.length === 0) { + setSnappedPosition(null); + setSnappedPoint(null); + } + canvasElement.addEventListener("mousedown", onMouseDown); + canvasElement.addEventListener("mouseup", onMouseUp); + canvasElement.addEventListener("mousemove", onMouseMove); + canvasElement.addEventListener("click", onMouseClick); + canvasElement.addEventListener("contextmenu", onContext); + } else { + setTempPoints([]); + setIsCreating(false); + canvasElement.removeEventListener("mousedown", onMouseDown); + canvasElement.removeEventListener("mouseup", onMouseUp); + canvasElement.removeEventListener("mousemove", onMouseMove); + canvasElement.removeEventListener("click", onMouseClick); + canvasElement.removeEventListener("contextmenu", onContext); + } + + 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, addFloor, getFloorPointById, getFloorByPoints, floorDepth, isBeveled, bevelStrength, sideMaterial, topMaterial, snappedPosition, snappedPoint]); + + return ( + <> + {toggleView && + <> + + {tempPoints.map((point) => ( + + ))} + + + {tempPoints.length > 0 && + + } + + } + + ) +} + +export default FloorCreator \ No newline at end of file diff --git a/app/src/modules/builder/floor/floorCreator/referenceFloor.tsx b/app/src/modules/builder/floor/floorCreator/referenceFloor.tsx new file mode 100644 index 0000000..71d376c --- /dev/null +++ b/app/src/modules/builder/floor/floorCreator/referenceFloor.tsx @@ -0,0 +1,165 @@ +import { useEffect, useRef, useState, useMemo } from 'react'; +import * as THREE from 'three'; +import { useFrame, useThree } from '@react-three/fiber'; +import { Extrude, Html } from '@react-three/drei'; +import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; +import { useActiveLayer, useToolMode, useToggleView } from '../../../../store/builder/store'; +import { useDirectionalSnapping } from '../../point/helpers/useDirectionalSnapping'; +import { usePointSnapping } from '../../point/helpers/usePointSnapping'; +import ReferenceLine from '../../line/reference/referenceLine'; + +interface ReferenceFloorProps { + tempPoints: Point[]; +} + +function ReferenceFloor({ tempPoints }: Readonly) { + const { floorDepth, isBeveled, bevelStrength, 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 [tempFloor, setTempFloor] = useState(null); + const [currentPosition, setCurrentPosition] = useState<[number, number, number]>(tempPoints[0]?.position); + + const directionalSnap = useDirectionalSnapping(currentPosition, tempPoints[tempPoints.length - 1]?.position || null); + const { snapFloorPoint } = usePointSnapping({ uuid: 'temp-Floor', pointType: 'Floor', position: directionalSnap.position || [0, 0, 0], }); + + useFrame(() => { + if (toolMode === 'Floor' && toggleView && tempPoints.length > 0) { + raycaster.setFromCamera(pointer, camera); + const intersectionPoint = new THREE.Vector3(); + raycaster.ray.intersectPlane(plane, intersectionPoint); + + setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]); + + if (!intersectionPoint) return; + const snapped = snapFloorPoint([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z], tempPoints.length > 2 ? [tempPoints[0]] : []); + + 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; + + const floorPoints: Point[] = [ + ...tempPoints, + { + pointUuid: 'temp-point', + pointType: 'Floor', + position: finalPosition.current, + layer: activeLayer, + }, + ]; + + setTempFloor({ + floorUuid: 'temp-floor', + floorName: 'temp-floor', + points: floorPoints, + topMaterial: 'default', + sideMaterial: 'default', + floorDepth, + bevelStrength, + isBeveled, + decals: [], + }); + + } else if (tempFloor !== null) { + setTempFloor(null); + } + }); + + useEffect(() => { + setTempFloor(null); + }, [toolMode, toggleView, tempPoints.length, floorDepth, bevelStrength, isBeveled, activeLayer]); + + const allLines = useMemo(() => { + if (!tempFloor || tempFloor.points.length < 2) return []; + + const lines: [Point, Point][] = []; + const pts = tempFloor.points; + + for (let i = 0; i < pts.length - 1; i++) { + lines.push([pts[i], pts[i + 1]]); + } + + return lines; + }, [tempFloor]); + + if (!tempFloor) return null; + + return ( + + {allLines.map(([p1, p2], idx) => { + const v1 = new THREE.Vector3(...p1.position); + const v2 = new THREE.Vector3(...p2.position); + const mid = new THREE.Vector3().addVectors(v1, v2).multiplyScalar(0.5); + const dist = v1.distanceTo(v2); + + return ( + + + {toggleView && ( + +
{dist.toFixed(2)} m
+ + )} +
+ ); + })} + + {tempPoints.length >= 3 && ( + + )} +
+ ); +} + +export default ReferenceFloor; + + +function Floor({ floor }: { floor: Point[] }) { + const savedTheme: string | null = localStorage.getItem('theme'); + + const shape = useMemo(() => { + const shape = new THREE.Shape(); + const points = floor.map(p => new THREE.Vector2(p.position[0], p.position[2])); + if (points.length < 3) return null; + shape.moveTo(points[0].x, points[0].y); + points.forEach((pt) => { shape.lineTo(pt.x, pt.y); }); + return shape; + }, [floor]); + + if (!shape) return null; + + return ( + + + + + + ); +} diff --git a/app/src/modules/builder/floor/floorGroup.tsx b/app/src/modules/builder/floor/floorGroup.tsx new file mode 100644 index 0000000..47ea3b5 --- /dev/null +++ b/app/src/modules/builder/floor/floorGroup.tsx @@ -0,0 +1,55 @@ +import { useEffect } from 'react'; +import { useActiveTool, useToggleView } from '../../../store/builder/store' +import { useBuilderStore } from '../../../store/builder/useBuilderStore'; +import { useVersionContext } from '../version/versionContext'; +import { useSceneContext } from '../../scene/sceneContext'; +import { useParams } from 'react-router-dom'; +import useModuleStore from '../../../store/useModuleStore'; +import FloorCreator from './floorCreator/floorCreator'; +import FloorInstances from './Instances/floorInstances'; +import { getFloorsApi } from '../../../services/factoryBuilder/floor/getFloorsApi'; + +function FloorGroup() { + const { togglView } = useToggleView(); + const { setSelectedFloor, setSelectedDecal } = useBuilderStore(); + const { activeModule } = useModuleStore(); + const { activeTool } = useActiveTool(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { floorStore } = useSceneContext(); + const { setFloors } = floorStore(); + const { projectId } = useParams(); + + useEffect(() => { + if (togglView || activeModule !== 'builder') { + setSelectedFloor(null); + setSelectedDecal(null); + } + }, [togglView, activeModule, activeTool]) + + useEffect(() => { + if (projectId && selectedVersion) { + getFloorsApi(projectId, selectedVersion?.versionId || '').then((floors) => { + if (floors && floors.length > 0) { + setFloors(floors); + } else { + setFloors([]); + } + }).catch((err) => { + console.log(err); + }) + } + }, [projectId, selectedVersion?.versionId]) + + return ( + <> + + + + + + + ) +} + +export default FloorGroup \ No newline at end of file diff --git a/app/src/modules/builder/functions/deletableLineOrPoint.ts b/app/src/modules/builder/functions/deletableLineOrPoint.ts deleted file mode 100644 index cbca682..0000000 --- a/app/src/modules/builder/functions/deletableLineOrPoint.ts +++ /dev/null @@ -1,87 +0,0 @@ -import * as THREE from 'three'; -import * as CONSTANTS from '../../../types/world/worldConstants'; -import * as Types from "../../../types/world/worldTypes"; - -function DeletableLineorPoint( - state: Types.ThreeState, - plane: Types.RefMesh, - floorPlanGroupLine: Types.RefGroup, - floorPlanGroupPoint: Types.RefGroup, - hoveredDeletableLine: Types.RefMesh, - hoveredDeletablePoint: Types.RefMesh -): void { - - ////////// Altering the color of the hovered line or point during the deletion time ////////// - - if (!plane.current) return; - let intersects = state.raycaster.intersectObject(plane.current, true); - - let visibleIntersectLines; - if (floorPlanGroupLine.current) { visibleIntersectLines = state.raycaster?.intersectObjects(floorPlanGroupLine.current.children, true); } - const visibleIntersectLine = visibleIntersectLines?.find(intersect => intersect.object.visible) as THREE.Line | undefined || null; - - let visibleIntersectPoints; - if (floorPlanGroupPoint.current) { - visibleIntersectPoints = state.raycaster?.intersectObjects(floorPlanGroupPoint.current.children, true); - } - const visibleIntersectPoint = visibleIntersectPoints?.find(intersect => intersect.object.visible) as THREE.Mesh | undefined; - - 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; - } - } - - if (intersects.length > 0) { - if (visibleIntersectPoint) { - if (hoveredDeletableLine.current) { - const lineType = hoveredDeletableLine.current.userData.linePoints[1]?.[3]; - const color = getLineColor(lineType); - (hoveredDeletableLine.current.material as THREE.MeshBasicMaterial).color = new THREE.Color(color); - hoveredDeletableLine.current = null; - } - - 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.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.uOuterColor.value.set((hoveredDeletablePoint.current as any).userData.color); - // hoveredDeletablePoint.current.scale.set(1, 1, 1); - hoveredDeletablePoint.current = null; - } - - if (visibleIntersectLine && !visibleIntersectPoint) { - if (hoveredDeletableLine.current) { - const lineType = hoveredDeletableLine.current.userData.linePoints[1]?.[3]; - const color = getLineColor(lineType); - (hoveredDeletableLine.current.material as THREE.MeshBasicMaterial).color = new THREE.Color(color); - hoveredDeletableLine.current = null; - } - - if (hoveredDeletablePoint.current) { - (hoveredDeletablePoint.current as any).material.uniforms.uInnerColor.value.set(CONSTANTS.pointConfig.defaultInnerColor); - (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; - } - - hoveredDeletableLine.current = (visibleIntersectLine as any).object; - if (hoveredDeletableLine.current) { - (hoveredDeletableLine.current.material as THREE.MeshBasicMaterial).color = new THREE.Color("red"); - } - } else if (hoveredDeletableLine.current) { - const lineType = hoveredDeletableLine.current.userData.linePoints[1]?.[3]; - const color = getLineColor(lineType); - (hoveredDeletableLine.current.material as THREE.MeshBasicMaterial).color = new THREE.Color(color); - hoveredDeletableLine.current = null; - } - } - -} - -export default DeletableLineorPoint; diff --git a/app/src/modules/builder/functions/draw.ts b/app/src/modules/builder/functions/draw.ts deleted file mode 100644 index 425fbbb..0000000 --- a/app/src/modules/builder/functions/draw.ts +++ /dev/null @@ -1,93 +0,0 @@ -import * as Types from "../../../types/world/worldTypes"; -import * as CONSTANTS from '../../../types/world/worldConstants'; -import createAndMoveReferenceLine from "../geomentries/lines/createAndMoveReferenceLine"; - -async function Draw( - state: Types.ThreeState, - plane: Types.RefMesh, - cursorPosition: Types.Vector3, - floorPlanGroupPoint: Types.RefGroup, - snappedPoint: Types.RefVector3, - isSnapped: Types.RefBoolean, - isSnappedUUID: Types.RefString, - line: Types.RefLine, - ispreSnapped: Types.RefBoolean, - floorPlanGroup: Types.RefGroup, - ReferenceLineMesh: Types.RefMesh, - LineCreated: Types.RefBoolean, - setRefTextUpdate: any, - Tube: Types.RefTubeGeometry, - anglesnappedPoint: Types.RefVector3, - isAngleSnapped: Types.RefBoolean, - toolMode: Types.String, -): Promise { - - ////////// Snapping the cursor during the drawing time and also changing the color of the intersected lines ////////// - - if (!plane.current) return; - const intersects = state.raycaster.intersectObject(plane.current, true); - - if (intersects.length > 0 && (toolMode === "Wall" || toolMode === "Floor")) { - const intersectionPoint = intersects[0].point; - cursorPosition.copy(intersectionPoint); - const snapThreshold = 1; - - if (line.current.length === 0) { - for (const point of floorPlanGroupPoint.current.children) { - const pointType = point.userData.type; - - const canSnap = - ((toolMode === "Wall") && (pointType === CONSTANTS.lineConfig.wallName || pointType === CONSTANTS.lineConfig.floorName)) || - ((toolMode === "Floor") && (pointType === CONSTANTS.lineConfig.wallName || pointType === CONSTANTS.lineConfig.floorName)) - - if (canSnap && cursorPosition.distanceTo(point.position) < snapThreshold + 0.5 && point.visible) { - cursorPosition.copy(point.position); - snappedPoint.current = point.position; - ispreSnapped.current = true; - isSnapped.current = false; - isSnappedUUID.current = point.uuid; - break; - } else { - ispreSnapped.current = false; - } - } - } else if (line.current.length > 0 && line.current[0]) { - for (const point of floorPlanGroupPoint.current.children) { - const pointType = point.userData.type; - - let canSnap = - ((toolMode === "Wall") && (pointType === CONSTANTS.lineConfig.wallName || pointType === CONSTANTS.lineConfig.floorName)) || - ((toolMode === "Floor") && (pointType === CONSTANTS.lineConfig.wallName || pointType === CONSTANTS.lineConfig.floorName)) - - if (canSnap && cursorPosition.distanceTo(point.position) < snapThreshold && point.visible) { - cursorPosition.copy(point.position); - snappedPoint.current = point.position; - isSnapped.current = true; - ispreSnapped.current = false; - isSnappedUUID.current = point.uuid; - break; - } else { - isSnapped.current = false; - } - } - - createAndMoveReferenceLine( - line.current[0][0], - cursorPosition, - isSnapped, - ispreSnapped, - line, - setRefTextUpdate, - floorPlanGroup, - ReferenceLineMesh, - LineCreated, - Tube, - anglesnappedPoint, - isAngleSnapped - ); - } - } - -} - -export default Draw; \ No newline at end of file diff --git a/app/src/modules/builder/geomentries/floors/addFloorToScene.ts b/app/src/modules/builder/geomentries/floors/addFloorToScene.ts deleted file mode 100644 index 71f2d24..0000000 --- a/app/src/modules/builder/geomentries/floors/addFloorToScene.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as THREE from 'three'; -import * as Types from "../../../../types/world/worldTypes"; -import * as CONSTANTS from "../../../../types/world/worldConstants"; - -import texturePath from "../../../../assets/textures/floor/white.png"; -import texturePathDark from "../../../../assets/textures/floor/black.png"; - -// Cache for materials -const materialCache = new Map(); - -export default function addFloorToScene( - shape: THREE.Shape, - layer: number, - floorGroup: Types.RefGroup, - userData: any, -) { - const savedTheme: string | null = localStorage.getItem('theme'); - - const textureLoader = new THREE.TextureLoader(); - - const textureScale = CONSTANTS.floorConfig.textureScale; - - const materialKey = `floorMaterial_${textureScale}`; - - let material: THREE.Material; - - if (materialCache.has(materialKey)) { - material = materialCache.get(materialKey) as THREE.Material; - } else { - const floorTexture = textureLoader.load(savedTheme === "dark" ? texturePathDark : texturePath); - // const floorTexture = textureLoader.load(texturePath); - - floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping; - floorTexture.repeat.set(textureScale, textureScale); - floorTexture.colorSpace = THREE.SRGBColorSpace; - - material = new THREE.MeshStandardMaterial({ - map: floorTexture, - side: THREE.DoubleSide, - }); - - materialCache.set(materialKey, material); - } - - const extrudeSettings = { - depth: CONSTANTS.floorConfig.height, - bevelEnabled: false, - }; - - const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); - const mesh = new THREE.Mesh(geometry, material); - - mesh.receiveShadow = true; - mesh.position.y = (layer) * CONSTANTS.wallConfig.height; - mesh.rotateX(Math.PI / 2); - mesh.name = `Floor_Layer_${layer}`; - - // Store UUIDs for debugging or future processing - mesh.userData.uuids = userData; - - floorGroup.current.add(mesh); -} diff --git a/app/src/modules/builder/geomentries/floors/drawOnlyFloor.ts b/app/src/modules/builder/geomentries/floors/drawOnlyFloor.ts deleted file mode 100644 index 74b06ef..0000000 --- a/app/src/modules/builder/geomentries/floors/drawOnlyFloor.ts +++ /dev/null @@ -1,275 +0,0 @@ -import * as THREE from "three"; - -import * as Types from "../../../../types/world/worldTypes"; -import * as CONSTANTS from "../../../../types/world/worldConstants"; - -import addPointToScene from "../points/addPointToScene"; -import addLineToScene from "../lines/addLineToScene"; -import splitLine from "../lines/splitLine"; -import removeReferenceLine from "../lines/removeReferenceLine"; -import getClosestIntersection from "../lines/getClosestIntersection"; -import arrayLineToObject from "../lines/lineConvertions/arrayLineToObject"; -// import { setLine } from '../../../../services/factoryBuilder/lines/setLineApi'; -import { Socket } from "socket.io-client"; -import { getUserData } from "../../../../functions/getUserData"; - -async function drawOnlyFloor( - raycaster: THREE.Raycaster, - state: Types.ThreeState, - camera: THREE.Camera, - plane: Types.RefMesh, - floorPlanGroupPoint: Types.RefGroup, - snappedPoint: Types.RefVector3, - isSnapped: Types.RefBoolean, - isSnappedUUID: Types.RefString, - line: Types.RefLine, - ispreSnapped: Types.RefBoolean, - anglesnappedPoint: Types.RefVector3, - isAngleSnapped: Types.RefBoolean, - onlyFloorline: Types.RefOnlyFloorLine, - onlyFloorlines: Types.RefOnlyFloorLines, - lines: Types.RefLines, - floorPlanGroupLine: Types.RefGroup, - floorPlanGroup: Types.RefGroup, - ReferenceLineMesh: Types.RefMesh, - LineCreated: Types.RefBoolean, - currentLayerPoint: Types.RefMeshArray, - dragPointControls: Types.RefDragControl, - setNewLines: any, - setDeletedLines: any, - activeLayer: Types.Number, - socket: Socket, - projectId?: string, - versionId?: string, -): Promise { - ////////// Creating lines Based on the positions clicked ////////// - - if (!plane.current) return; - const { userId, organization, email } = getUserData(); - const intersects = raycaster.intersectObject(plane.current, true); - const intersectsLines = raycaster.intersectObjects( - floorPlanGroupLine.current.children, - true - ); - const intersectsPoint = raycaster.intersectObjects( - floorPlanGroupPoint.current.children, - true - ); - const VisibleintersectsPoint = intersectsPoint.find( - (intersect) => intersect.object.visible - ); - const visibleIntersect = intersectsLines.find( - (intersect) => - intersect.object.visible && - intersect.object.name !== CONSTANTS.lineConfig.referenceName - ); - if ( - (intersectsPoint.length === 0 || VisibleintersectsPoint === undefined) && - intersectsLines.length > 0 && - !isSnapped.current && - !ispreSnapped.current - ) { - ////////// Clicked on a preexisting Line ////////// - - if ( - visibleIntersect && - (intersectsLines[0].object.userData.linePoints[0][3] === - CONSTANTS.lineConfig.floorName || - intersectsLines[0].object.userData.linePoints[0][3] === - CONSTANTS.lineConfig.wallName) - ) { - let pointColor, lineColor; - if ( - intersectsLines[0].object.userData.linePoints[0][3] === - CONSTANTS.lineConfig.wallName - ) { - pointColor = CONSTANTS.pointConfig.wallOuterColor; - lineColor = CONSTANTS.lineConfig.wallColor; - } else { - pointColor = CONSTANTS.pointConfig.floorOuterColor; - lineColor = CONSTANTS.lineConfig.floorColor; - } - let IntersectsPoint = new THREE.Vector3( - intersects[0].point.x, - 0.01, - intersects[0].point.z - ); - if ( - isAngleSnapped.current && - line.current.length > 0 && - anglesnappedPoint.current - ) { - IntersectsPoint = anglesnappedPoint.current; - } - if (visibleIntersect.object instanceof THREE.Mesh) { - const ThroughPoint = - visibleIntersect.object.geometry.parameters.path.getPoints( - CONSTANTS.lineConfig.lineIntersectionPoints - ); - let intersectionPoint = getClosestIntersection( - ThroughPoint, - IntersectsPoint - ); - - if (intersectionPoint) { - const newLines = splitLine( - visibleIntersect, - intersectionPoint, - currentLayerPoint, - floorPlanGroupPoint, - dragPointControls, - isSnappedUUID, - lines, - setDeletedLines, - floorPlanGroupLine, - socket, - pointColor, - lineColor, - intersectsLines[0].object.userData.linePoints[0][3], - projectId - ); - setNewLines([newLines[0], newLines[1]]); - - (line.current as Types.Line).push([ - new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z), - isSnappedUUID.current!, - activeLayer, - CONSTANTS.lineConfig.floorName, - ]); - - if (line.current.length >= 2 && line.current[0] && line.current[1]) { - lines.current.push(line.current as Types.Line); - const data = arrayLineToObject(line.current as Types.Line); - - //REST - - // setLine(organization, data.layer!, data.line!, data.type!); - - //SOCKET - - const input = { - organization, - layer: data.layer, - line: data.line, - type: data.type, - socketId: socket.id, - projectId, - versionId, - userId, - }; - - console.log('input: ', input); - socket.emit("v1:Line:create", input); - - setNewLines([newLines[0], newLines[1], line.current]); - onlyFloorline.current.push(line.current as Types.Line); - onlyFloorlines.current.push(onlyFloorline.current); - onlyFloorline.current = []; - - addLineToScene( - line.current[0][0], - line.current[1][0], - CONSTANTS.lineConfig.floorColor, - line.current, - floorPlanGroupLine - ); - - removeReferenceLine( - floorPlanGroup, - ReferenceLineMesh, - LineCreated, - line - ); - } - return; - } - } - } - } - if (intersects.length > 0 && intersectsLines.length === 0) { - ////////// Clicked on an empty place or a point ////////// - - let intersectionPoint = intersects[0].point; - - if ( - isAngleSnapped.current && - line.current.length > 0 && - anglesnappedPoint.current - ) { - intersectionPoint = anglesnappedPoint.current; - } - if (isSnapped.current && line.current.length > 0 && snappedPoint.current) { - intersectionPoint = snappedPoint.current; - } - if (ispreSnapped.current && snappedPoint.current) { - intersectionPoint = snappedPoint.current; - } - - if (!isSnapped.current && !ispreSnapped.current) { - addPointToScene( - intersectionPoint, - CONSTANTS.pointConfig.floorOuterColor, - currentLayerPoint, - floorPlanGroupPoint, - dragPointControls, - isSnappedUUID, - CONSTANTS.lineConfig.floorName - ); - } else { - ispreSnapped.current = false; - isSnapped.current = false; - } - - (line.current as Types.Line).push([ - new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z), - isSnappedUUID.current!, - activeLayer, - CONSTANTS.lineConfig.floorName, - ]); - - if (line.current.length >= 2 && line.current[0] && line.current[1]) { - onlyFloorline.current.push(line.current as Types.Line); - lines.current.push(line.current as Types.Line); - const data = arrayLineToObject(line.current as Types.Line); - - //REST - - // setLine(organization, data.layer!, data.line!, data.type!); - - //SOCKET - - const input = { - organization, - layer: data.layer, - line: data.line, - type: data.type, - socketId: socket.id, - versionId, - projectId, - userId, - }; - - console.log('input: ', input); - socket.emit("v1:Line:create", input); - - setNewLines([line.current]); - addLineToScene( - line.current[0][0], - line.current[1][0], - CONSTANTS.lineConfig.floorColor, - line.current, - floorPlanGroupLine - ); - const lastPoint = line.current[line.current.length - 1]; - line.current = [lastPoint]; - } - if (isSnapped.current) { - ////////// Add this to stop the drawing mode after snapping ////////// - removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line); - onlyFloorlines.current.push(onlyFloorline.current); - onlyFloorline.current = []; - } - } -} - -export default drawOnlyFloor; diff --git a/app/src/modules/builder/geomentries/floors/loadFloor.ts b/app/src/modules/builder/geomentries/floors/loadFloor.ts deleted file mode 100644 index d4f2fc1..0000000 --- a/app/src/modules/builder/geomentries/floors/loadFloor.ts +++ /dev/null @@ -1,50 +0,0 @@ -import * as THREE from 'three'; -import * as CONSTANTS from '../../../../types/world/worldConstants'; -import addRoofToScene from '../roofs/addRoofToScene'; - -import * as Types from "../../../../types/world/worldTypes"; -import loadOnlyFloors from './loadOnlyFloors'; -import addFloorToScene from './addFloorToScene'; -import getRoomsFromLines from '../lines/getRoomsFromLines'; - -async function loadFloor( - lines: Types.RefLines, - floorGroup: Types.RefGroup, -): Promise { - - if (!floorGroup.current) return; - - floorGroup.current.children = []; - - if (lines.current.length > 2) { - const linesByLayer = lines.current.reduce((acc: { [key: number]: any[] }, pair) => { - const layer = pair[0][2]; - if (!acc[layer]) acc[layer] = []; - acc[layer].push(pair); - return acc; - }, {}); - - for (const layer in linesByLayer) { - // Only Floor Polygons - loadOnlyFloors(floorGroup, linesByLayer, layer); - - const rooms: Types.Rooms = await getRoomsFromLines({ current: linesByLayer[layer] }); - - rooms.forEach(({ coordinates: room, layer }) => { - const userData = room.map(point => point.uuid); - const shape = new THREE.Shape(); - shape.moveTo(room[0].position.x, room[0].position.z); - room.forEach(point => shape.lineTo(point.position.x, point.position.z)); - shape.closePath(); - - // Floor Polygons - addFloorToScene(shape, (layer - 1) * CONSTANTS.wallConfig.height, floorGroup, userData); - - // Roof Polygons - addRoofToScene(shape, (layer - 1) * CONSTANTS.wallConfig.height, userData, floorGroup); - }); - } - } -} - -export default loadFloor; diff --git a/app/src/modules/builder/geomentries/floors/loadOnlyFloors.ts b/app/src/modules/builder/geomentries/floors/loadOnlyFloors.ts deleted file mode 100644 index 3da7b46..0000000 --- a/app/src/modules/builder/geomentries/floors/loadOnlyFloors.ts +++ /dev/null @@ -1,183 +0,0 @@ -import * as THREE from 'three'; -import * as turf from '@turf/turf'; -import * as CONSTANTS from '../../../../types/world/worldConstants'; -import * as Types from "../../../../types/world/worldTypes"; - -function loadOnlyFloors( - floorGroup: Types.RefGroup, - linesByLayer: any, - layer: any, -): void { - - ////////// Creating polygon floor based on the onlyFloorlines.current which does not add roof to it, The lines are still stored in Lines.current as well ////////// - - let floorsInLayer = linesByLayer[layer]; - floorsInLayer = floorsInLayer.filter((line: any) => line[0][3] && line[1][3] === CONSTANTS.lineConfig.floorName); - const floorResult = floorsInLayer.map((pair: [THREE.Vector3, string, number, string][]) => - pair.map((point) => ({ - position: [point[0].x, point[0].z], - uuid: point[1] - })) - ); - const FloorLineFeatures = floorResult.map((line: any) => turf.lineString(line.map((p: any) => p.position))); - - function identifyPolygonsAndConnectedLines(FloorLineFeatures: any) { - const floorpolygons = []; - const connectedLines = []; - const unprocessedLines = [...FloorLineFeatures]; // Copy the features - - while (unprocessedLines.length > 0) { - const currentLine = unprocessedLines.pop(); - const coordinates = currentLine.geometry.coordinates; - - // Check if the line is closed (forms a polygon) - if ( - coordinates[0][0] === coordinates[coordinates.length - 1][0] && - coordinates[0][1] === coordinates[coordinates.length - 1][1] - ) { - floorpolygons.push(turf.polygon([coordinates])); // Add as a polygon - continue; - } - - // Check if the line connects to another line - let connected = false; - for (let i = unprocessedLines.length - 1; i >= 0; i--) { - const otherCoordinates = unprocessedLines[i].geometry.coordinates; - - // Check if lines share a start or end point - if ( - coordinates[0][0] === otherCoordinates[otherCoordinates.length - 1][0] && - coordinates[0][1] === otherCoordinates[otherCoordinates.length - 1][1] - ) { - // Merge lines - const mergedCoordinates = [...otherCoordinates, ...coordinates.slice(1)]; - unprocessedLines[i] = turf.lineString(mergedCoordinates); - connected = true; - break; - } else if ( - coordinates[coordinates.length - 1][0] === otherCoordinates[0][0] && - coordinates[coordinates.length - 1][1] === otherCoordinates[0][1] - ) { - // Merge lines - const mergedCoordinates = [...coordinates, ...otherCoordinates.slice(1)]; - unprocessedLines[i] = turf.lineString(mergedCoordinates); - connected = true; - break; - } - } - - if (!connected) { - connectedLines.push(currentLine); // Add unconnected line as-is - } - } - - return { floorpolygons, connectedLines }; - } - - const { floorpolygons, connectedLines } = identifyPolygonsAndConnectedLines(FloorLineFeatures); - - function convertConnectedLinesToPolygons(connectedLines: any) { - return connectedLines.map((line: any) => { - const coordinates = line.geometry.coordinates; - - // If the line has more than two points, close the polygon - if (coordinates.length > 2) { - const firstPoint = coordinates[0]; - const lastPoint = coordinates[coordinates.length - 1]; - - // Check if already closed; if not, close it - if (firstPoint[0] !== lastPoint[0] || firstPoint[1] !== lastPoint[1]) { - coordinates.push(firstPoint); - } - - // Convert the closed line into a polygon - return turf.polygon([coordinates]); - } - - // If not enough points for a polygon, return the line unchanged - return line; - }); - } - - const convertedConnectedPolygons = convertConnectedLinesToPolygons(connectedLines); - - if (convertedConnectedPolygons.length > 0) { - const validPolygons = convertedConnectedPolygons.filter( - (polygon: any) => polygon.geometry?.type === "Polygon" - ); - - if (validPolygons.length > 0) { - floorpolygons.push(...validPolygons); - } - } - - function convertPolygonsToOriginalFormat(floorpolygons: any, originalLines: [THREE.Vector3, string, number, string][][]) { - return floorpolygons.map((polygon: any) => { - const coordinates = polygon.geometry.coordinates[0]; // Extract the coordinates array (assume it's a single polygon) - - // Map each coordinate back to its original structure - const mappedPoints = coordinates.map((coord: [number, number]) => { - const [x, z] = coord; - - // Find the original point matching this coordinate - const originalPoint = originalLines.flat().find(([point]) => point.x === x && point.z === z); - - if (!originalPoint) { - console.error(`Original point for coordinate [${x}, ${z}] not found.`); - } - - return originalPoint; - }); - - // Create pairs of consecutive points - const pairs: typeof originalLines = []; - for (let i = 0; i < mappedPoints.length - 1; i++) { - pairs.push([mappedPoints[i], mappedPoints[i + 1]]); - } - - return pairs; - }); - } - - const convertedFloorPolygons: Types.OnlyFloorLines = convertPolygonsToOriginalFormat(floorpolygons, floorsInLayer); - - convertedFloorPolygons.forEach((floor) => { - const points: THREE.Vector3[] = []; - - floor.forEach((lineSegment) => { - const startPoint = lineSegment[0][0]; - points.push(new THREE.Vector3(startPoint.x, startPoint.y, startPoint.z)); - }); - - const lastLine = floor[floor.length - 1]; - const endPoint = lastLine[1][0]; - points.push(new THREE.Vector3(endPoint.x, endPoint.y, endPoint.z)); - - const shape = new THREE.Shape(); - shape.moveTo(points[0].x, points[0].z); - - points.forEach(point => shape.lineTo(point.x, point.z)); - shape.closePath(); - - const extrudeSettings = { - depth: CONSTANTS.floorConfig.height, - bevelEnabled: false - }; - - const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); - const material = new THREE.MeshStandardMaterial({ color: CONSTANTS.floorConfig.defaultColor, side: THREE.DoubleSide }); - const mesh = new THREE.Mesh(geometry, material); - - mesh.castShadow = true; - mesh.receiveShadow = true; - - mesh.position.y = (floor[0][0][2] - 1) * CONSTANTS.wallConfig.height; - mesh.rotateX(Math.PI / 2); - mesh.name = `Only_Floor_Line_${floor[0][0][2]}`; - - mesh.userData = floor; - floorGroup?.current?.add(mesh); - }); -} - -export default loadOnlyFloors; diff --git a/app/src/modules/builder/geomentries/floors/updateFloorLines.ts b/app/src/modules/builder/geomentries/floors/updateFloorLines.ts deleted file mode 100644 index 4198a00..0000000 --- a/app/src/modules/builder/geomentries/floors/updateFloorLines.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as Types from "../../../../types/world/worldTypes"; - -function updateFloorLines( - onlyFloorlines: Types.RefOnlyFloorLines, - DragedPoint: Types.Mesh | { uuid: string, position: Types.Vector3 } -): void { - - ////////// Update onlyFloorlines.current if it contains the dragged point ////////// - - onlyFloorlines.current.forEach((floorline) => { - floorline.forEach((line) => { - line.forEach((point) => { - const [position, uuid] = point; - if (uuid === DragedPoint.uuid) { - position.x = DragedPoint.position.x; - position.y = 0.01; - position.z = DragedPoint.position.z; - } - }); - }); - }); -} - -export default updateFloorLines; diff --git a/app/src/modules/builder/geomentries/layers/deleteLayer.ts b/app/src/modules/builder/geomentries/layers/deleteLayer.ts deleted file mode 100644 index 318cc61..0000000 --- a/app/src/modules/builder/geomentries/layers/deleteLayer.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { toast } from "react-toastify"; -import RemoveConnectedLines from "../lines/removeConnectedLines"; - -import * as Types from "../../../../types/world/worldTypes"; -import { Socket } from "socket.io-client"; -import { getUserData } from "../../../../functions/getUserData"; -// import { deleteLayer } from '../../../../services/factoryBuilder/lines/deleteLayerApi'; - -async function DeleteLayer( - removedLayer: Types.Number, - lines: Types.RefLines, - floorPlanGroupLine: Types.RefGroup, - floorPlanGroupPoint: Types.RefGroup, - onlyFloorlines: Types.RefOnlyFloorLines, - floorGroup: Types.RefGroup, - setDeletedLines: any, - setRemovedLayer: Types.setRemoveLayerSetState, - socket: Socket, - projectId?: string, - versionId?: string, -): Promise { - ////////// Remove the Lines from the lines.current based on the removed layer and rearrange the layer number that are higher than the removed layer ////////// - - const removedLines: Types.Lines = lines.current.filter( - (line) => line[0][2] === removedLayer - ); - const { userId, organization } = getUserData(); - - //REST - - // await deleteLayer(organization, removedLayer); - - //SOCKET - - const data = { - organization, - layer: removedLayer, - socketId: socket.id, - versionId, - projectId, - userId, - }; - - socket.emit("v1:Line:delete:layer", data); - - ////////// Remove Points and lines from the removed layer ////////// - - removedLines.forEach((line) => { - line.forEach((removedPoint) => { - RemoveConnectedLines( - removedPoint[1], - floorPlanGroupLine, - floorPlanGroupPoint, - setDeletedLines, - lines - ); - }); - }); - - ////////// Update the remaining lines layer values in the userData and in lines.current ////////// - - let remaining = lines.current.filter((line) => line[0][2] !== removedLayer); - let updatedLines: Types.Lines = []; - remaining.forEach((line) => { - let newLines: Types.Line = [...line]; - if (newLines[0][2] > removedLayer) { - newLines[0][2] -= 1; - newLines[1][2] -= 1; - } - - const matchingLine = floorPlanGroupLine.current.children.find( - (l) => - l.userData.linePoints[0][1] === line[0][1] && - l.userData.linePoints[1][1] === line[1][1] - ); - if (matchingLine) { - const updatedUserData = matchingLine.userData; - updatedUserData.linePoints[0][2] = newLines[0][2]; - updatedUserData.linePoints[1][2] = newLines[1][2]; - } - 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) => { - return floor[0][0][2] !== removedLayer; - }); - const meshToRemove: any = floorGroup.current?.children.find( - (mesh) => mesh.name === `Only_Floor_Line_${removedLayer}` - ); - if (meshToRemove) { - (meshToRemove.material).dispose(); - (meshToRemove.geometry).dispose(); - floorGroup.current?.remove(meshToRemove); - } - - echo.success("Layer Removed!"); - setRemovedLayer(null); -} -export default DeleteLayer; diff --git a/app/src/modules/builder/geomentries/layers/layer2DVisibility.ts b/app/src/modules/builder/geomentries/layers/layer2DVisibility.ts deleted file mode 100644 index f1828de..0000000 --- a/app/src/modules/builder/geomentries/layers/layer2DVisibility.ts +++ /dev/null @@ -1,35 +0,0 @@ -import * as Types from "../../../../types/world/worldTypes"; - -function Layer2DVisibility( - activeLayer: Types.Number, - floorPlanGroup: Types.RefGroup, - floorPlanGroupLine: Types.RefGroup, - floorPlanGroupPoint: Types.RefGroup, - currentLayerPoint: Types.RefMeshArray, - dragPointControls: Types.RefDragControl -): void { - - if (floorPlanGroup.current && dragPointControls.current) { - currentLayerPoint.current = []; - floorPlanGroupLine.current.children.forEach((line) => { - const linePoints = line.userData.linePoints; - - const point1 = floorPlanGroupPoint.current.getObjectByProperty('uuid', linePoints[0][1]) as Types.Mesh; - const point2 = floorPlanGroupPoint.current.getObjectByProperty('uuid', linePoints[1][1]) as Types.Mesh; - - if (linePoints[0][2] !== activeLayer && linePoints[1][2] !== activeLayer) { - point1.visible = false; - point2.visible = false; - line.visible = false; - } else { - point1.visible = true; - point2.visible = true; - line.visible = true; - currentLayerPoint.current.push(point1, point2); - } - }); - dragPointControls.current!.objects = currentLayerPoint.current; - } -} - -export default Layer2DVisibility; diff --git a/app/src/modules/builder/geomentries/lines/addLineToScene.ts b/app/src/modules/builder/geomentries/lines/addLineToScene.ts deleted file mode 100644 index 9e30e1d..0000000 --- a/app/src/modules/builder/geomentries/lines/addLineToScene.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as THREE from "three"; -import * as CONSTANTS from '../../../../types/world/worldConstants'; -import * as Types from "../../../../types/world/worldTypes"; - -function addLineToScene( - start: Types.Vector3, - end: Types.Vector3, - colour: Types.Color, - userData: Types.UserData, - floorPlanGroupLine: Types.RefGroup -): void { - - ////////// A function that creates and adds lines based on the start, end, and colour from the params, Also adds the userData in the mesh userData ////////// - - const path = new THREE.CatmullRomCurve3([start, end]); - const geometry = new THREE.TubeGeometry(path, CONSTANTS.lineConfig.tubularSegments, CONSTANTS.lineConfig.radius, CONSTANTS.lineConfig.radialSegments, false); - const material = new THREE.MeshBasicMaterial({ color: colour }); - const mesh = new THREE.Mesh(geometry, material); - floorPlanGroupLine.current.add(mesh); - - mesh.userData.linePoints = userData; -} - -export default addLineToScene; diff --git a/app/src/modules/builder/geomentries/lines/createAndMoveReferenceLine.ts b/app/src/modules/builder/geomentries/lines/createAndMoveReferenceLine.ts deleted file mode 100644 index e5b6fbe..0000000 --- a/app/src/modules/builder/geomentries/lines/createAndMoveReferenceLine.ts +++ /dev/null @@ -1,98 +0,0 @@ -import * as THREE from "three"; -import * as CONSTANTS from '../../../../types/world/worldConstants'; -import * as Types from "../../../../types/world/worldTypes"; - -function createAndMoveReferenceLine( - point: Types.Vector3, - cursorPosition: Types.Vector3, - isSnapped: Types.RefBoolean, - ispreSnapped: Types.RefBoolean, - line: Types.RefLine, - setRefTextUpdate: Types.NumberIncrementState, - floorPlanGroup: Types.RefGroup, - ReferenceLineMesh: Types.RefMesh, - LineCreated: Types.RefBoolean, - Tube: Types.RefTubeGeometry, - anglesnappedPoint: Types.RefVector3, - isAngleSnapped: Types.RefBoolean -): void { - - ////////// Creating new and maintaining the old reference line and also snap the reference line based on its angle ////////// - - const startPoint = point; - - const dx = cursorPosition.x - startPoint.x; - const dz = cursorPosition.z - startPoint.z; - let angle = Math.atan2(dz, dx); - - angle = (angle * 180) / Math.PI; - angle = (angle + 360) % 360; - - const snapAngles = [0, 90, 180, 270, 360]; - const snapThreshold = 2.5; - - const closestSnapAngle = snapAngles.reduce((prev, curr) => - Math.abs(curr - angle) < Math.abs(prev - angle) ? curr : prev - ); - - if (!isSnapped.current && !ispreSnapped.current && line.current.length > 0) { - if (Math.abs(closestSnapAngle - angle) <= snapThreshold) { - const snappedAngleRad = (closestSnapAngle * Math.PI) / 180; - const distance = Math.sqrt(dx * dx + dz * dz); - const snappedX = startPoint.x + distance * Math.cos(snappedAngleRad); - const snappedZ = startPoint.z + distance * Math.sin(snappedAngleRad); - - if ( - cursorPosition.distanceTo( - new THREE.Vector3(snappedX, 0.01, snappedZ) - ) < 2 - ) { - cursorPosition.set(snappedX, 0.01, snappedZ); - isAngleSnapped.current = true; - anglesnappedPoint.current = new THREE.Vector3( - snappedX, - 0.01, - snappedZ - ); - } else { - isAngleSnapped.current = false; - anglesnappedPoint.current = null; - } - } else { - isAngleSnapped.current = false; - anglesnappedPoint.current = null; - } - } else { - isAngleSnapped.current = false; - anglesnappedPoint.current = null; - } - - if (!LineCreated.current) { - setRefTextUpdate((prevUpdate) => prevUpdate - 1); - const path = new THREE.LineCurve3(startPoint, cursorPosition); - Tube.current = new THREE.TubeGeometry(path, CONSTANTS.lineConfig.tubularSegments, CONSTANTS.lineConfig.radius, CONSTANTS.lineConfig.radialSegments, false); - const material = new THREE.MeshBasicMaterial({ color: CONSTANTS.lineConfig.helperColor }); - ReferenceLineMesh.current = new THREE.Mesh(Tube.current, material); - ReferenceLineMesh.current.name = CONSTANTS.lineConfig.referenceName; - ReferenceLineMesh.current.userData = { - linePoints: { startPoint, cursorPosition }, - }; - floorPlanGroup.current?.add(ReferenceLineMesh.current); - LineCreated.current = true; - } else { - if (ReferenceLineMesh.current) { - const path = new THREE.LineCurve3(startPoint, new THREE.Vector3(cursorPosition.x, 0.01, cursorPosition.z)); - Tube.current = new THREE.TubeGeometry(path, CONSTANTS.lineConfig.tubularSegments, CONSTANTS.lineConfig.radius, CONSTANTS.lineConfig.radialSegments, false); - - if (ReferenceLineMesh.current) { - ReferenceLineMesh.current.userData = { - linePoints: { startPoint, cursorPosition }, - }; - ReferenceLineMesh.current.geometry.dispose(); - ReferenceLineMesh.current.geometry = Tube.current; - } - } - } -} - -export default createAndMoveReferenceLine; diff --git a/app/src/modules/builder/geomentries/lines/deleteLine.ts b/app/src/modules/builder/geomentries/lines/deleteLine.ts deleted file mode 100644 index 4b5158f..0000000 --- a/app/src/modules/builder/geomentries/lines/deleteLine.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { Socket } from "socket.io-client"; -// import { deleteLineApi } from "../../../../services/factoryBuilder/lines/deleteLineApi"; -import * as Types from "../../../../types/world/worldTypes"; - -import { toast } from "react-toastify"; -import { getUserData } from "../../../../functions/getUserData"; - -function deleteLine( - hoveredDeletableLine: Types.RefMesh, - onlyFloorlines: Types.RefOnlyFloorLines, - lines: Types.RefLines, - floorPlanGroupLine: Types.RefGroup, - floorPlanGroupPoint: Types.RefGroup, - setDeletedLines: any, - socket: Socket, - projectId?: string, - versionId?: string, -): void { - const { userId, organization, email } = getUserData(); - ////////// Deleting a line and the points if they are not connected to any other line ////////// - - if (!hoveredDeletableLine.current) { - return; - } - - const linePoints = hoveredDeletableLine.current.userData.linePoints; - const connectedpoints = [linePoints[0][1], linePoints[1][1]]; - - //REST - - // deleteLineApi( - // organization, - // [ - // { "uuid": linePoints[0][1] }, - // { "uuid": linePoints[1][1] } - // ] - // ) - - //SOCKET - - const data = { - organization, - line: [{ uuid: linePoints[0][1] }, { uuid: linePoints[1][1] }], - socketId: socket.id, - versionId, - projectId, - userId, - }; - - socket.emit("v1:Line:delete", data); - - onlyFloorlines.current = onlyFloorlines.current - .map((floorline) => - floorline.filter( - (line) => - line[0][1] !== connectedpoints[0] && line[1][1] !== connectedpoints[1] - ) - ) - .filter((floorline) => floorline.length > 0); - - lines.current = lines.current.filter((item) => item !== linePoints); - (hoveredDeletableLine.current.material).dispose(); - (hoveredDeletableLine.current.geometry).dispose(); - floorPlanGroupLine.current.remove(hoveredDeletableLine.current); - setDeletedLines([linePoints]); - - connectedpoints.forEach((pointUUID) => { - let isConnected = false; - floorPlanGroupLine.current.children.forEach((line) => { - 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); - } - }); - } - }); - - echo.success("Line Removed!"); -} - -export default deleteLine; diff --git a/app/src/modules/builder/geomentries/lines/distanceText/distanceText.tsx b/app/src/modules/builder/geomentries/lines/distanceText/distanceText.tsx deleted file mode 100644 index cdb586e..0000000 --- a/app/src/modules/builder/geomentries/lines/distanceText/distanceText.tsx +++ /dev/null @@ -1,226 +0,0 @@ -import { useEffect, useState } from "react"; -import { getLines } from "../../../../../services/factoryBuilder/lines/getLinesApi"; -import * as THREE from "three"; -import { - useActiveLayer, - useDeletedLines, - useNewLines, - useRoomsState, - useToggleView, -} from "../../../../../store/builder/store"; -import objectLinesToArray from "../lineConvertions/objectLinesToArray"; -import { Html } from "@react-three/drei"; -import { Vector2 } from "three"; -import * as Types from "../../../../../types/world/worldTypes"; -import getRoomsFromLines from "../getRoomsFromLines"; -import * as turf from '@turf/turf'; -import { useParams } from "react-router-dom"; -import { getUserData } from "../../../../../functions/getUserData"; -import { useVersionContext } from "../../../version/versionContext"; - -const DistanceText = () => { - const [lines, setLines] = useState< - { - distance: string; - position: THREE.Vector3; - userData: Types.Line; - layer: string; - }[] - >([]); - const { activeLayer } = useActiveLayer(); - const { toggleView } = useToggleView(); - const { newLines, setNewLines } = useNewLines(); - const { deletedLines, setDeletedLines } = useDeletedLines(); - const [linesState, setLinesState] = useState([]); - const { roomsState, setRoomsState } = useRoomsState(); - const { selectedVersionStore } = useVersionContext(); - const { selectedVersion } = selectedVersionStore(); - const { projectId } = useParams(); - const { organization, email } = getUserData(); - - useEffect(() => { - - if (linesState.length === 0) return; - const getLines = async () => { - const points3D = linesState.map(line => { - const startPoint = line[0][0]; // First point of each wall line - return [startPoint.x, 0, startPoint.z]; - }); - - // Ensure the polygon is closed - if ( - points3D[0][0] !== points3D[points3D.length - 1][0] || - points3D[0][1] !== points3D[points3D.length - 1][1] - ) { - points3D.push(points3D[0]); - } - - // Convert to 2D for turf (x, z) - const coords2D = points3D.map(p => [p[0], p[1]]); - - const projected = points3D.map((p: any) => new Vector2(p[0], p[1])); - - // Shoelace formula for 2D polygon - let area = 0; - const n = projected.length; - for (let i = 0; i < n - 1; i++) { - const curr = projected[i]; - const next = projected[i + 1]; - area += curr.x * next.y - next.x * curr.y; - - } - - // return Math.abs(area) / 2; - - // Build polygon and compute area - // const polygon = turf.polygon([coords2D]); - // const area = turf.area(polygon); - // const area = computeAreaFrom3DPoints(coords2D) - - // - if (lines.length > 2) { - const linesByLayer = linesState.reduce((acc: { [key: number]: any[] }, pair) => { - const layer = pair[0][2]; - if (!acc[layer]) acc[layer] = []; - acc[layer].push(pair); - return acc; - }, {}); - - - for (const layer in linesByLayer) { - const rooms: Types.Rooms = await getRoomsFromLines({ current: linesByLayer[layer] }); - setRoomsState(rooms) - } - } - } - getLines(); - }, [linesState, roomsState]) - - - useEffect(() => { - if (!email || !selectedVersion) return; - - getLines(organization, projectId, selectedVersion?.versionId || '').then((data) => { - data = objectLinesToArray(data); - setLinesState(data); - - const lines = data - .filter((line: Types.Line) => line[0][2] === activeLayer) - .map((line: Types.Line) => { - const point1 = new THREE.Vector3( - line[0][0].x, - line[0][0].y, - line[0][0].z - ); - const point2 = new THREE.Vector3( - line[1][0].x, - line[1][0].y, - line[1][0].z - ); - const distance = point1.distanceTo(point2); - const midpoint = new THREE.Vector3() - .addVectors(point1, point2) - .divideScalar(2); - return { - distance: distance.toFixed(1), - position: midpoint, - userData: line, - layer: activeLayer, - }; - }); - setLines(lines); - }); - }, [activeLayer, selectedVersion?.versionId]); - - useEffect(() => { - if (newLines.length > 0) { - if (newLines[0][0][2] !== activeLayer) return; - const newLinesData = newLines.map((line: Types.Line) => { - const point1 = new THREE.Vector3( - line[0][0].x, - line[0][0].y, - line[0][0].z - ); - const point2 = new THREE.Vector3( - line[1][0].x, - line[1][0].y, - line[1][0].z - ); - const distance = point1.distanceTo(point2); - const midpoint = new THREE.Vector3() - .addVectors(point1, point2) - .divideScalar(2); - - return { - distance: distance.toFixed(1), - position: midpoint, - userData: line, - layer: activeLayer, - }; - }); - setLines((prevLines) => [...prevLines, ...newLinesData]); - setLinesState((prevLines) => [...prevLines, ...newLines]); - setNewLines([]); - } - }, [newLines, activeLayer]); - - useEffect(() => { - if ((deletedLines as Types.Lines).length > 0) { - setLines((prevLines) => - prevLines.filter( - (line) => - !deletedLines.some( - (deletedLine: any) => - deletedLine[0][1] === line.userData[0][1] && - deletedLine[1][1] === line.userData[1][1] - ) - ) - ); - - setLinesState(prev => - prev.filter(line => - !(deletedLines as Types.Lines).some( - deleted => - line[0][1] === deleted[0][1] && line[1][1] === deleted[1][1] - ) - ) - ); - - setDeletedLines([]); - setRoomsState([]) - } - }, [deletedLines]); - - return ( - <> - {toggleView && ( - - {lines.map((text) => ( - -
- {text.distance} m -
- - ))} -
- )} - - ); -}; - -export default DistanceText; diff --git a/app/src/modules/builder/geomentries/lines/distanceText/referenceDistanceText.tsx b/app/src/modules/builder/geomentries/lines/distanceText/referenceDistanceText.tsx deleted file mode 100644 index ce9864d..0000000 --- a/app/src/modules/builder/geomentries/lines/distanceText/referenceDistanceText.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import * as THREE from "three"; -import { Html } from "@react-three/drei"; -import { useState, useEffect } from "react"; -import { useActiveLayer } from "../../../../../store/builder/store"; - -const ReferenceDistanceText = ({ line }: { line: any }) => { - interface TextState { - distance: string; - position: THREE.Vector3; - userData: any; - layer: any; - } - - const [text, setTexts] = useState(null); - const { activeLayer } = useActiveLayer(); - - useEffect(() => { - if (line) { - if (line.parent === null) { - setTexts(null); - return; - } - const distance = line.userData.linePoints.cursorPosition.distanceTo( - line.userData.linePoints.startPoint - ); - const midpoint = new THREE.Vector3() - .addVectors( - line.userData.linePoints.cursorPosition, - line.userData.linePoints.startPoint - ) - .divideScalar(2); - const newTexts = { - distance: distance.toFixed(1), - position: midpoint, - userData: line, - layer: activeLayer, - }; - setTexts(newTexts); - } - }); - - return ( - - - {text !== null && ( - -
- {text.distance} m -
- - )} -
-
- ); -}; - -export default ReferenceDistanceText; diff --git a/app/src/modules/builder/geomentries/lines/drawWall.ts b/app/src/modules/builder/geomentries/lines/drawWall.ts deleted file mode 100644 index 7807610..0000000 --- a/app/src/modules/builder/geomentries/lines/drawWall.ts +++ /dev/null @@ -1,331 +0,0 @@ -import * as THREE from "three"; -import * as CONSTANTS from "../../../../types/world/worldConstants"; - -import addPointToScene from "../points/addPointToScene"; -import addLineToScene from "./addLineToScene"; -import splitLine from "./splitLine"; -import removeReferenceLine from "./removeReferenceLine"; -import getClosestIntersection from "./getClosestIntersection"; - -import * as Types from "../../../../types/world/worldTypes"; -import arrayLineToObject from "./lineConvertions/arrayLineToObject"; -// import { setLine } from '../../../../services/factoryBuilder/lines/setLineApi'; -import { Socket } from "socket.io-client"; -import { getUserData } from "../../../../functions/getUserData"; -import * as Y from 'yjs'; -import { generateUniqueId } from "../../../../functions/generateUniqueId"; - -async function drawWall( - raycaster: THREE.Raycaster, - plane: Types.RefMesh, - floorPlanGroupPoint: Types.RefGroup, - snappedPoint: Types.RefVector3, - isSnapped: Types.RefBoolean, - isSnappedUUID: Types.RefString, - line: Types.RefLine, - ispreSnapped: Types.RefBoolean, - anglesnappedPoint: Types.RefVector3, - isAngleSnapped: Types.RefBoolean, - lines: Types.RefLines, - floorPlanGroupLine: Types.RefGroup, - floorPlanGroup: Types.RefGroup, - ReferenceLineMesh: Types.RefMesh, - LineCreated: Types.RefBoolean, - currentLayerPoint: Types.RefMeshArray, - dragPointControls: Types.RefDragControl, - setNewLines: any, - setDeletedLines: any, - activeLayer: Types.Number, - socket: Socket, - projectId?: string, - versionId?: string, - ydoc?: any -): Promise { - const { userId, organization } = getUserData(); - ////////// Creating lines Based on the positions clicked ////////// - - ////////// Allows the user lines that represents walls and roof, floor if forms a polygon ////////// - - if (!plane.current) return; - let intersects = raycaster.intersectObject(plane.current, true); - - let intersectsLines = raycaster.intersectObjects(floorPlanGroupLine.current.children, true); - let intersectsPoint = raycaster.intersectObjects(floorPlanGroupPoint.current.children, true); - - const VisibleintersectsPoint = intersectsPoint.find((intersect) => intersect.object.visible); - const visibleIntersect = intersectsLines.find( - (intersect) => - intersect.object.visible && - intersect.object.name !== CONSTANTS.lineConfig.referenceName && - intersect.object.userData.linePoints[0][3] === - CONSTANTS.lineConfig.wallName - ); - - if ((intersectsPoint.length === 0 || VisibleintersectsPoint === undefined) && intersectsLines.length > 0 && !isSnapped.current && !ispreSnapped.current) { - ////////// Clicked on a preexisting Line ////////// - - if (visibleIntersect && intersects) { - let IntersectsPoint = new THREE.Vector3(intersects[0].point.x, 0.01, intersects[0].point.z); - - if (isAngleSnapped.current && anglesnappedPoint.current) { - IntersectsPoint = anglesnappedPoint.current; - } - if (visibleIntersect.object instanceof THREE.Mesh) { - const ThroughPoint = visibleIntersect.object.geometry.parameters.path.getPoints(CONSTANTS.lineConfig.lineIntersectionPoints); - let intersectionPoint = getClosestIntersection(ThroughPoint, IntersectsPoint); - - if (intersectionPoint) { - const newLines = splitLine( - visibleIntersect, - intersectionPoint, - currentLayerPoint, - floorPlanGroupPoint, - dragPointControls, - isSnappedUUID, - lines, - setDeletedLines, - floorPlanGroupLine, - socket, - CONSTANTS.pointConfig.wallOuterColor, - CONSTANTS.lineConfig.wallColor, - CONSTANTS.lineConfig.wallName, - projectId, - versionId - ); - setNewLines([newLines[0], newLines[1]]); - - (line.current as Types.Line).push([ - new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z), - isSnappedUUID.current!, - activeLayer, - CONSTANTS.lineConfig.wallName, - ]); - - if (line.current.length >= 2 && line.current[0] && line.current[1]) { - const data = arrayLineToObject(line.current as Types.Line); - - //REST - - // setLine(organization, data.layer!, data.line!, data.type!); - - //SOCKET - - const input = { - organization, - layer: data.layer, - line: data.line, - type: data.type, - socketId: socket.id, - versionId, - projectId, - userId, - }; - - console.log('input: ', input); - socket.emit("v1:Line-collab:create", input); - - // ✅ Add to Yjs map (will auto-sync to other clients) - const yLine = new Y.Map(); - yLine.set("organization", organization); - yLine.set("layer", data.layer); - yLine.set("type", data.type); - yLine.set("line", data.line) - yLine.set("socketId", socket.id) - yLine.set("projectId", projectId); - yLine.set("versionId", versionId); - yLine.set("userId", userId); - - const yLineArray = new Y.Array(); - if (!data.line) return - for (const point of data.line) { - const yPoint = new Y.Map(); - yPoint.set("uuid", point.uuid); - if (point.position) { - yPoint.set("position", new Y.Map([ - ["x", point.position.x], - ["y", point.position.y], - ["z", point.position.z], - ])); - } - yLineArray.push([yPoint]); - } - yLine.set("line", yLineArray); - - const lineMap = ydoc.getMap("lines"); - lineMap.observe((event: any) => { - console.log('event: ', event); - console.log("Map changed!", event.changes.keys); - }); - - const lineId = crypto.randomUUID(); // Or use backend _id if available - lineMap.set(lineId, yLine); // 👈 triggers Yjs update event - - - - setNewLines([newLines[0], newLines[1], line.current]); - lines.current.push(line.current as Types.Line); - addLineToScene( - line.current[0][0], - line.current[1][0], - CONSTANTS.lineConfig.wallColor, - line.current, - floorPlanGroupLine - ); - let lastPoint = line.current[line.current.length - 1]; - line.current = [lastPoint]; - } - - - return; - } - } - } - } - - if (intersects && intersects.length > 0) { - ////////// Clicked on a emply place or a point ////////// - - let intersectionPoint = intersects[0].point; - - if (isAngleSnapped.current && line.current.length > 0 && anglesnappedPoint.current) { - intersectionPoint = anglesnappedPoint.current; - } - if (isSnapped.current && line.current.length > 0 && snappedPoint.current) { - intersectionPoint = snappedPoint.current; - } - if (ispreSnapped.current && snappedPoint.current) { - intersectionPoint = snappedPoint.current; - } - - if (!isSnapped.current && !ispreSnapped.current) { - addPointToScene( - intersectionPoint, - CONSTANTS.pointConfig.wallOuterColor, - currentLayerPoint, - floorPlanGroupPoint, - dragPointControls, - isSnappedUUID, - CONSTANTS.lineConfig.wallName - ); - } else { - ispreSnapped.current = false; - isSnapped.current = false; - } - - (line.current as Types.Line).push([ - new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z), - isSnappedUUID.current!, - activeLayer, - CONSTANTS.lineConfig.wallName, - ]); - - // if (line.current.length >= 2 && line.current[0] && line.current[1]) { - // const data = arrayLineToObject(line.current as Types.Line); - - // //REST - - // // setLine(organization, data.layer!, data.line!, data.type!); - - // //SOCKET - - // const input = { - // organization, - // layer: data.layer, - // line: data.line, - // type: data.type, - // socketId: socket.id, - // versionId, - // projectId, - // userId, - // }; - - // console.log('input: ', input); - // socket.emit("v1:Line-collab:create", input); - - // setNewLines([line.current]); - // lines.current.push(line.current as Types.Line); - // addLineToScene( - // line.current[0][0], - // line.current[1][0], - // CONSTANTS.lineConfig.wallColor, - // line.current, - // floorPlanGroupLine - // ); - // let lastPoint = line.current[line.current.length - 1]; - // line.current = [lastPoint]; - // } - if (line.current.length >= 2 && line.current[0] && line.current[1]) { - const data = arrayLineToObject(line.current as Types.Line); - - // ✅ Prepare input for socket.emit (your backend) - const input = { - organization, - layer: data.layer, - line: data.line, - type: data.type, - socketId: socket.id, - versionId, - projectId, - userId, - }; - - // ✅ Emit to backend - socket.emit("v1:Line-collab:create", input); - - // ✅ Add to Yjs map (will auto-sync to other clients) - const yLine = new Y.Map(); - yLine.set("organization", organization); - yLine.set("layer", data.layer); - yLine.set("type", data.type); - yLine.set("line", data.line) - yLine.set("socketId", socket.id) - yLine.set("projectId", projectId); - yLine.set("versionId", versionId); - yLine.set("userId", userId); - - const yLineArray = new Y.Array(); - if (!data.line) return - for (const point of data.line) { - const yPoint = new Y.Map(); - yPoint.set("uuid", point.uuid); - if (point.position) { - yPoint.set("position", new Y.Map([ - ["x", point.position.x], - ["y", point.position.y], - ["z", point.position.z], - ])); - } - yLineArray.push([yPoint]); - } - yLine.set("line", yLineArray); - - const lineMap = ydoc.getMap("lines"); - lineMap.observe((event: any) => { - console.log('event: ', event); - console.log("Map changed!", event.changes.keys); - }); - - const lineId = generateUniqueId(); // Or use backend _id if available - lineMap.set(lineId, yLine); // 👈 triggers Yjs update event - - // ✅ Scene + local updates - setNewLines([line.current]); - lines.current.push(line.current as Types.Line); - addLineToScene( - line.current[0][0], - line.current[1][0], - CONSTANTS.lineConfig.wallColor, - line.current, - floorPlanGroupLine - ); - - const lastPoint = line.current[line.current.length - 1]; - line.current = [lastPoint]; - } - if (isSnapped.current) { - removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line); - } - } -} - -export default drawWall; diff --git a/app/src/modules/builder/geomentries/lines/getRoomsFromLines.ts b/app/src/modules/builder/geomentries/lines/getRoomsFromLines.ts deleted file mode 100644 index 08304ea..0000000 --- a/app/src/modules/builder/geomentries/lines/getRoomsFromLines.ts +++ /dev/null @@ -1,86 +0,0 @@ -import * as THREE from 'three'; -import * as turf from '@turf/turf'; -import * as CONSTANTS from '../../../../types/world/worldConstants'; -import * as Types from "../../../../types/world/worldTypes"; - -async function getRoomsFromLines(lines: Types.RefLines) { - const rooms: Types.Rooms = []; - - if (lines.current.length > 2) { - const linesByLayer = lines.current.reduce((acc: { [key: number]: any[] }, pair) => { - const layer = pair[0][2]; - if (!acc[layer]) acc[layer] = []; - acc[layer].push(pair); - return acc; - }, {}); - - ////////// Use turf.polygonize to create polygons from the line points ////////// - - for (const layer in linesByLayer) { - - let linesInLayer = linesByLayer[layer]; - linesInLayer = linesInLayer.filter(line => line[0][3] && line[1][3] === CONSTANTS.lineConfig.wallName); - const result = linesInLayer.map((pair: [THREE.Vector3, string, number, string][]) => - pair.map((point) => ({ - position: [point[0].x, point[0].z], - uuid: point[1] - })) - ); - const lineFeatures = result.map(line => turf.lineString(line.map(p => p.position))); - const polygons = turf.polygonize(turf.featureCollection(lineFeatures)); - - let union: any[] = []; - - polygons.features.forEach((feature) => { - union.push(feature); - }); - - if (union.length > 1) { - const unionResult = turf.union(turf.featureCollection(union)); - if (unionResult?.geometry.type === "MultiPolygon") { - unionResult?.geometry.coordinates.forEach((poly) => { - const Coordinates = poly[0].map(([x, z]) => { - const matchingPoint = result.flat().find(r => - r.position[0].toFixed(10) === x.toFixed(10) && - r.position[1].toFixed(10) === z.toFixed(10) - ); - return { - position: new THREE.Vector3(x, 0, z), - uuid: matchingPoint ? matchingPoint.uuid : '' - }; - }); - rooms.push({ coordinates: Coordinates.reverse(), layer: parseInt(layer) }); - }); - } else if (unionResult?.geometry.type === "Polygon") { - const Coordinates = unionResult?.geometry.coordinates[0].map(([x, z]) => { - const matchingPoint = result.flat().find(r => - r.position[0].toFixed(10) === x.toFixed(10) && - r.position[1].toFixed(10) === z.toFixed(10) - ); - return { - position: new THREE.Vector3(x, 0, z), - uuid: matchingPoint ? matchingPoint.uuid : '' - }; - }); - rooms.push({ coordinates: Coordinates.reverse(), layer: parseInt(layer) }); - } - } else if (union.length === 1) { - const Coordinates = union[0].geometry.coordinates[0].map(([x, z]: [number, number]) => { - const matchingPoint = result.flat().find(r => - r.position[0].toFixed(10) === x.toFixed(10) && - r.position[1].toFixed(10) === z.toFixed(10) - ); - return { - position: new THREE.Vector3(x, 0, z), - uuid: matchingPoint ? matchingPoint.uuid : '' - }; - }); - rooms.push({ coordinates: Coordinates, layer: parseInt(layer) }); - } - } - } - - return rooms; -} - -export default getRoomsFromLines; diff --git a/app/src/modules/builder/geomentries/lines/lineConvertions/arrayLineToObject.ts b/app/src/modules/builder/geomentries/lines/lineConvertions/arrayLineToObject.ts deleted file mode 100644 index dffac05..0000000 --- a/app/src/modules/builder/geomentries/lines/lineConvertions/arrayLineToObject.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as Types from "../../../../../types/world/worldTypes"; - -export default function arrayLineToObject(array: Types.Line) { - if (!Array.isArray(array)) { - return {}; - } - - // Extract common properties from the first point - const commonLayer = array[0][2]; - const commonType = array[0][3]; - - // Map points into a structured format - const line = array.map(([position, uuid]) => ({ - position, - uuid, - })); - - // Create the final structured object - return { - layer: commonLayer, - type: commonType, - line, - }; -} \ No newline at end of file diff --git a/app/src/modules/builder/geomentries/lines/lineConvertions/arrayLinesToObject.ts b/app/src/modules/builder/geomentries/lines/lineConvertions/arrayLinesToObject.ts deleted file mode 100644 index 041b17c..0000000 --- a/app/src/modules/builder/geomentries/lines/lineConvertions/arrayLinesToObject.ts +++ /dev/null @@ -1,30 +0,0 @@ -import * as Types from "../../../../../types/world/worldTypes"; - -export default function arrayLinesToObject(array: Array) { - if (!Array.isArray(array)) { - return []; - } - - return array.map((lineArray) => { - if (!Array.isArray(lineArray)) { - return null; - } - - // Extract common properties from the first point - const commonLayer = lineArray[0][2]; - const commonType = lineArray[0][3]; - - // Map points into a structured format - const line = lineArray.map(([position, uuid]) => ({ - position, - uuid, - })); - - // Create the final structured object - return { - layer: commonLayer, - type: commonType, - line, - }; - }).filter((item) => item !== null); // Filter out invalid entries -} diff --git a/app/src/modules/builder/geomentries/lines/lineConvertions/objectLineToArray.ts b/app/src/modules/builder/geomentries/lines/lineConvertions/objectLineToArray.ts deleted file mode 100644 index ac0162c..0000000 --- a/app/src/modules/builder/geomentries/lines/lineConvertions/objectLineToArray.ts +++ /dev/null @@ -1,13 +0,0 @@ -import * as THREE from 'three'; - -export default function objectLineToArray(structuredObject: any) { - if (!structuredObject || !structuredObject.line) { - return []; - } - - // Destructure common properties - const { layer, type, line } = structuredObject; - - // Map points back to the original array format - return line.map(({ position, uuid }: any) => [new THREE.Vector3(position.x, position.y, position.z), uuid, layer, type]); -} \ No newline at end of file diff --git a/app/src/modules/builder/geomentries/lines/lineConvertions/objectLinesToArray.ts b/app/src/modules/builder/geomentries/lines/lineConvertions/objectLinesToArray.ts deleted file mode 100644 index 856b935..0000000 --- a/app/src/modules/builder/geomentries/lines/lineConvertions/objectLinesToArray.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as THREE from 'three'; - -export default function objectLinesToArray(structuredObjects: any): any { - if (!Array.isArray(structuredObjects)) { - return []; - } - - return structuredObjects.map((structuredObject) => { - if (!structuredObject || !structuredObject.line) { - return []; - } - - const { layer, type, line } = structuredObject; - - return line.map(({ position, uuid }: any) => { - const vector = new THREE.Vector3(position.x, position.y, position.z); - return [vector, uuid, layer, type]; - }); - }); -} diff --git a/app/src/modules/builder/geomentries/lines/removeConnectedLines.ts b/app/src/modules/builder/geomentries/lines/removeConnectedLines.ts deleted file mode 100644 index ddf6452..0000000 --- a/app/src/modules/builder/geomentries/lines/removeConnectedLines.ts +++ /dev/null @@ -1,66 +0,0 @@ -import * as THREE from 'three'; - -import * as Types from "../../../../types/world/worldTypes"; - -function RemoveConnectedLines( - DeletedPointUUID: Types.String, - floorPlanGroupLine: Types.RefGroup, - floorPlanGroupPoint: Types.RefGroup, - setDeletedLines: any, - lines: Types.RefLines, -): void { - - ////////// Check if any and how many lines are connected to the deleted point ////////// - - const removableLines: THREE.Mesh[] = []; - const connectedpoints: string[] = []; - - const removedLinePoints: [number, string, number][][] = []; // Array to hold linePoints of removed lines - - floorPlanGroupLine.current.children.forEach((line) => { - const linePoints = line.userData.linePoints as [number, string, number][]; - const uuid1 = linePoints[0][1]; - const uuid2 = linePoints[1][1]; - - if (uuid1 === DeletedPointUUID || uuid2 === DeletedPointUUID) { - connectedpoints.push(uuid1 === DeletedPointUUID ? uuid2 : uuid1); - removableLines.push(line as THREE.Mesh); - removedLinePoints.push(linePoints); - } - }); - - if (removableLines.length > 0) { - removableLines.forEach((line) => { - lines.current = lines.current.filter(item => item !== line.userData.linePoints); - (line.material).dispose(); - (line.geometry).dispose(); - floorPlanGroupLine.current.remove(line); - }); - } - setDeletedLines(removedLinePoints) - - ////////// Check and Remove point that are no longer connected to any lines ////////// - - connectedpoints.forEach((pointUUID) => { - let isConnected = false; - floorPlanGroupLine.current.children.forEach((line) => { - const linePoints = line.userData.linePoints as [number, string, number][]; - 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); - } - }); - } - }); -} - -export default RemoveConnectedLines; diff --git a/app/src/modules/builder/geomentries/lines/removeReferenceLine.ts b/app/src/modules/builder/geomentries/lines/removeReferenceLine.ts deleted file mode 100644 index 787389a..0000000 --- a/app/src/modules/builder/geomentries/lines/removeReferenceLine.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as Types from "../../../../types/world/worldTypes"; - -function removeReferenceLine( - floorPlanGroup: Types.RefGroup, - ReferenceLineMesh: Types.RefMesh, - LineCreated: Types.RefBoolean, - line: Types.RefLine -): void { - - ////////// Removes Dangling reference line if the draw mode is ended or any other case ////////// - - line.current = []; - if (ReferenceLineMesh.current) { - (ReferenceLineMesh.current.material).dispose(); - (ReferenceLineMesh.current.geometry).dispose(); - floorPlanGroup.current.remove(ReferenceLineMesh.current); - LineCreated.current = false; - ReferenceLineMesh.current = undefined; - } -} - -export default removeReferenceLine; \ No newline at end of file diff --git a/app/src/modules/builder/geomentries/lines/splitLine.ts b/app/src/modules/builder/geomentries/lines/splitLine.ts deleted file mode 100644 index 31c2646..0000000 --- a/app/src/modules/builder/geomentries/lines/splitLine.ts +++ /dev/null @@ -1,153 +0,0 @@ -import * as THREE from "three"; - -import addLineToScene from "./addLineToScene"; -import addPointToScene from "../points/addPointToScene"; - -import * as Types from "../../../../types/world/worldTypes"; -import arrayLineToObject from "../lines/lineConvertions/arrayLineToObject"; -import { Socket } from "socket.io-client"; -import { getUserData } from "../../../../functions/getUserData"; -// import { deleteLineApi } from '../../../../services/factoryBuilder/lines/deleteLineApi'; -// import { setLine } from '../../../../services/factoryBuilder/lines/setLineApi'; - -function splitLine( - visibleIntersect: Types.IntersectionEvent, - intersectionPoint: Types.Vector3, - currentLayerPoint: Types.RefMeshArray, - floorPlanGroupPoint: Types.RefGroup, - dragPointControls: Types.RefDragControl, - isSnappedUUID: Types.RefString, - lines: Types.RefLines, - setDeletedLines: any, - floorPlanGroupLine: { current: THREE.Group }, - socket: Socket, - pointColor: Types.String, - lineColor: Types.String, - lineType: Types.String, - projectId?: string, - versionId?: string, -): [Types.Line, Types.Line] { - ////////// Removing the clicked line and splitting it with the clicked position adding a new point and two new lines ////////// - - const { userId, organization, email } = getUserData(); - (visibleIntersect.object as any).material.dispose(); - (visibleIntersect.object as any).geometry.dispose(); - floorPlanGroupLine.current.remove(visibleIntersect.object); - setDeletedLines([visibleIntersect.object.userData.linePoints]); - - //REST - - // deleteLineApi( - // organization, - // [ - // { "uuid": visibleIntersect.object.userData.linePoints[0][1] }, - // { "uuid": visibleIntersect.object.userData.linePoints[1][1] } - // ] - // ) - - //SOCKET - - const data = { - organization, - line: [ - { uuid: visibleIntersect.object.userData.linePoints[0][1] }, - { uuid: visibleIntersect.object.userData.linePoints[1][1] }, - ], - socketId: socket.id, - versionId, - projectId, - userId, - }; - - socket.emit("v1:Line:delete", data); - - const point = addPointToScene( - intersectionPoint, - pointColor, - currentLayerPoint, - floorPlanGroupPoint, - dragPointControls, - isSnappedUUID, - lineType - ); - - const oldLinePoints = visibleIntersect.object.userData.linePoints; - lines.current = lines.current.filter((item) => item !== oldLinePoints); - - const clickedPoint: Types.Point = [ - new THREE.Vector3(intersectionPoint.x, 0.01, intersectionPoint.z), - point.uuid, - oldLinePoints[0][2], - lineType, - ]; - - const start = oldLinePoints[0]; - const end = oldLinePoints[1]; - - const newLine1: Types.Line = [start, clickedPoint]; - const newLine2: Types.Line = [clickedPoint, end]; - - const line1 = arrayLineToObject(newLine1); - const line2 = arrayLineToObject(newLine2); - - //REST - - // setLine(organization, line1.layer!, line1.line!, line1.type!); - - //SOCKET - - const input1 = { - organization, - layer: line1.layer, - line: line1.line, - type: line1.type, - socketId: socket.id, - versionId, - projectId, - userId, - }; - - console.log('input1: ', input1); - socket.emit("v1:Line:create", input1); - - //REST - - // setLine(organization, line2.layer!, line2.line!, line2.type!); - - //SOCKET - - const input2 = { - organization, - layer: line2.layer, - line: line2.line, - type: line2.type, - socketId: socket.id, - versionId, - projectId, - userId, - }; - - console.log('input2: ', input2); - socket.emit("v1:Line:create", input2); - - lines.current.push(newLine1, newLine2); - - addLineToScene( - newLine1[0][0], - newLine1[1][0], - lineColor, - newLine1, - floorPlanGroupLine - ); - addLineToScene( - newLine2[0][0], - newLine2[1][0], - lineColor, - newLine2, - floorPlanGroupLine - ); - - return [newLine1, newLine2]; -} - -export default splitLine; diff --git a/app/src/modules/builder/geomentries/lines/updateDistanceText.ts b/app/src/modules/builder/geomentries/lines/updateDistanceText.ts deleted file mode 100644 index 8d7fbe2..0000000 --- a/app/src/modules/builder/geomentries/lines/updateDistanceText.ts +++ /dev/null @@ -1,42 +0,0 @@ -import * as THREE from 'three'; - -import * as Types from "../../../../types/world/worldTypes"; - -function updateDistanceText( - scene: THREE.Scene, - floorPlanGroupLine: Types.RefGroup, - affectedLines: Types.NumberArray -): void { - - ////////// Updating the Distance Texts of the lines that are affected during drag ////////// - - const DistanceGroup = scene.getObjectByName('Distance_Text') as THREE.Group; - - affectedLines.forEach((lineIndex) => { - const mesh = floorPlanGroupLine.current.children[lineIndex] as THREE.Mesh; - const linePoints = mesh.userData.linePoints; - - if (linePoints) { - const distance = linePoints[0][0].distanceTo(linePoints[1][0]).toFixed(1); - const position = new THREE.Vector3().addVectors(linePoints[0][0], linePoints[1][0]).divideScalar(2); - - if (!DistanceGroup || !linePoints) { - return - } - - DistanceGroup.children.forEach((text) => { - const textMesh = text as THREE.Mesh; - if (textMesh.userData[0][1] === linePoints[0][1] && textMesh.userData[1][1] === linePoints[1][1]) { - textMesh.position.set(position.x, 1, position.z); - const className = `distance line-${textMesh.userData[0][1]}_${textMesh.userData[1][1]}_${linePoints[0][2]}`; - const element = document.getElementsByClassName(className)[0] as HTMLElement; - if (element) { - element.innerHTML = `${distance} m`; - } - } - }); - } - }); -} - -export default updateDistanceText; diff --git a/app/src/modules/builder/geomentries/lines/updateLines.ts b/app/src/modules/builder/geomentries/lines/updateLines.ts deleted file mode 100644 index 0c4f22f..0000000 --- a/app/src/modules/builder/geomentries/lines/updateLines.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as THREE from 'three'; -import * as Types from "../../../../types/world/worldTypes"; -import * as CONSTANTS from '../../../../types/world/worldConstants'; - -function updateLines( - floorPlanGroupLine: Types.RefGroup, - affectedLines: Types.NumberArray -): void { - - ////////// Updating the positions for the affected lines only based on the updated positions ////////// - - affectedLines.forEach((lineIndex) => { - const mesh = floorPlanGroupLine.current.children[lineIndex] as Types.Mesh; - const linePoints = mesh.userData.linePoints as Types.Line; - if (linePoints) { - const newPositions = linePoints.map(([pos]) => pos); - const newPath = new THREE.CatmullRomCurve3(newPositions); - mesh.geometry.dispose(); - mesh.geometry = new THREE.TubeGeometry(newPath, CONSTANTS.lineConfig.tubularSegments, CONSTANTS.lineConfig.radius, CONSTANTS.lineConfig.radialSegments, false); - } - }); -} - -export default updateLines; \ No newline at end of file diff --git a/app/src/modules/builder/geomentries/lines/updateLinesPositions.ts b/app/src/modules/builder/geomentries/lines/updateLinesPositions.ts deleted file mode 100644 index 6411a09..0000000 --- a/app/src/modules/builder/geomentries/lines/updateLinesPositions.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as Types from "../../../../types/world/worldTypes"; - -function updateLinesPositions( - DragedPoint: Types.Mesh | { uuid: string, position: Types.Vector3 }, - lines: Types.RefLines -): Types.NumberArray { - - ////////// Updating the lines position based on the dragged point's position ////////// - - const objectUUID = DragedPoint.uuid; - const affectedLines: Types.NumberArray = []; - - lines.current.forEach((line, index) => { - let lineUpdated = false; - line.forEach((point) => { - const [position, uuid] = point; - if (uuid === objectUUID) { - position.x = DragedPoint.position.x; - position.y = 0.01; - position.z = DragedPoint.position.z; - lineUpdated = true; - } - }); - if (lineUpdated) { - affectedLines.push(index); - } - }); - - return affectedLines; -} - -export default updateLinesPositions; diff --git a/app/src/modules/builder/geomentries/lines/vectorizeLinesCurrent.ts b/app/src/modules/builder/geomentries/lines/vectorizeLinesCurrent.ts deleted file mode 100644 index a404a07..0000000 --- a/app/src/modules/builder/geomentries/lines/vectorizeLinesCurrent.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as THREE from 'three'; - -import * as Types from "../../../../types/world/worldTypes"; - -function vectorizeLinesCurrent( - lines: Types.Lines -): Types.Lines { - - ////////// Storing a vector3 array in localstorage makes the prototype functions go puff. This function brings back the prototype functions by creating it again ////////// - - return lines.map((line) => { - const p1: Types.Point = [new THREE.Vector3(line[0][0].x, line[0][0].y, line[0][0].z), line[0][1], line[0][2], line[0][3],]; - const p2: Types.Point = [new THREE.Vector3(line[1][0].x, line[1][0].y, line[1][0].z), line[1][1], line[0][2], line[1][3],]; - return [p1, p2]; - }); -} - -export default vectorizeLinesCurrent; diff --git a/app/src/modules/builder/geomentries/pillars/addAndUpdateReferencePillar.ts b/app/src/modules/builder/geomentries/pillars/addAndUpdateReferencePillar.ts deleted file mode 100644 index 04cad47..0000000 --- a/app/src/modules/builder/geomentries/pillars/addAndUpdateReferencePillar.ts +++ /dev/null @@ -1,54 +0,0 @@ -import * as THREE from 'three'; -import updateReferencePolesheight from './updateReferencePolesheight'; - -import * as Types from "../../../../types/world/worldTypes"; - -function addAndUpdateReferencePillar( - raycaster: THREE.Raycaster, - floorGroup: Types.RefGroup, - referencePole: Types.RefMesh -): void { - - ////////// Find Pillars position and scale based on the pointer interaction ////////// - - let Roofs = raycaster.intersectObjects(floorGroup.current.children, true); - const intersected = Roofs.find(intersect => intersect.object.name.includes("Roof") || intersect.object.name.includes("Floor")); - - if (intersected) { - const intersectionPoint = intersected.point; - raycaster.ray.origin.copy(intersectionPoint); - raycaster.ray.direction.set(0, -1, 0); - const belowIntersections = raycaster.intersectObjects(floorGroup.current.children, true); - const validIntersections = belowIntersections.filter(intersect => intersect.object.name.includes("Floor")); - - let distance: Types.Number; - - if (validIntersections.length > 1) { - let valid = validIntersections.find(intersectedBelow => intersected.point.distanceTo(intersectedBelow.point) > 3); - if (valid) { - updateReferencePolesheight(intersectionPoint, valid.distance, referencePole, floorGroup); - } else { - const belowPoint = new THREE.Vector3(intersectionPoint.x, 0, intersectionPoint.z); - distance = intersected.point.distanceTo(belowPoint); - if (distance > 3) { - updateReferencePolesheight(intersectionPoint, distance, referencePole, floorGroup); - } - } - } else { - const belowPoint = new THREE.Vector3(intersectionPoint.x, 0, intersectionPoint.z); - distance = intersected.point.distanceTo(belowPoint); - if (distance > 3) { - updateReferencePolesheight(intersectionPoint, distance, referencePole, floorGroup); - } - } - } else { - if (referencePole.current) { - (referencePole.current.material).dispose(); - (referencePole.current.geometry).dispose(); - floorGroup.current.remove(referencePole.current); - referencePole.current = null; - } - } -} - -export default addAndUpdateReferencePillar; diff --git a/app/src/modules/builder/geomentries/pillars/addPillar.ts b/app/src/modules/builder/geomentries/pillars/addPillar.ts deleted file mode 100644 index e6e4b86..0000000 --- a/app/src/modules/builder/geomentries/pillars/addPillar.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as THREE from 'three'; -import * as CONSTANTS from '../../../../types/world/worldConstants'; -import * as Types from "../../../../types/world/worldTypes"; - -function addPillar( - referencePole: Types.RefMesh, - floorGroup: Types.RefGroup -): void { - - ////////// Add Pillars to the scene based on the reference. current poles position and scale ////////// - - if (referencePole.current) { - let pole: THREE.Mesh; - const geometry = referencePole.current.userData.geometry.clone(); - const material = new THREE.MeshStandardMaterial({ color: CONSTANTS.columnConfig.defaultColor }); - pole = new THREE.Mesh(geometry, material); - pole.rotateX(Math.PI / 2); - pole.name = "Pole"; - pole.position.set(referencePole.current.userData.position.x, referencePole.current.userData.position.y, referencePole.current.userData.position.z); - floorGroup.current.add(pole); - } -} - -export default addPillar; \ No newline at end of file diff --git a/app/src/modules/builder/geomentries/pillars/deletableHoveredPillar.ts b/app/src/modules/builder/geomentries/pillars/deletableHoveredPillar.ts deleted file mode 100644 index 4fb65c0..0000000 --- a/app/src/modules/builder/geomentries/pillars/deletableHoveredPillar.ts +++ /dev/null @@ -1,34 +0,0 @@ -import * as THREE from 'three'; - -import * as Types from "../../../../types/world/worldTypes"; - -function DeletableHoveredPillar( - state: Types.ThreeState, - floorGroup: Types.RefGroup, - hoveredDeletablePillar: Types.RefMesh -): void { - - ////////// Altering the color of the hovered Pillar during the Deletion time ////////// - - const intersects = state.raycaster.intersectObjects(floorGroup.current.children, true); - const poleIntersect = intersects.find(intersect => intersect.object.name === "Pole"); - - if (poleIntersect) { - if (poleIntersect.object.name !== "Pole") { - return; - } - if (hoveredDeletablePillar.current) { - (hoveredDeletablePillar.current.material as THREE.MeshStandardMaterial).emissive = new THREE.Color("black"); - hoveredDeletablePillar.current = undefined; - } - hoveredDeletablePillar.current = poleIntersect.object as THREE.Mesh; // Type assertion - (hoveredDeletablePillar.current.material as THREE.MeshStandardMaterial).emissive = new THREE.Color("red"); - } else { - if (hoveredDeletablePillar.current) { - (hoveredDeletablePillar.current.material as THREE.MeshStandardMaterial).emissive = new THREE.Color("black"); - hoveredDeletablePillar.current = undefined; - } - } -} - -export default DeletableHoveredPillar; \ No newline at end of file diff --git a/app/src/modules/builder/geomentries/pillars/deletePillar.ts b/app/src/modules/builder/geomentries/pillars/deletePillar.ts deleted file mode 100644 index 39e0b28..0000000 --- a/app/src/modules/builder/geomentries/pillars/deletePillar.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { toast } from 'react-toastify'; - -import * as Types from "../../../../types/world/worldTypes"; - -function DeletePillar( - hoveredDeletablePillar: Types.RefMesh, - floorGroup: Types.RefGroup -): void { - - ////////// Deleting the hovered Pillar from the itemsGroup ////////// - - if (hoveredDeletablePillar.current) { - (hoveredDeletablePillar.current.material).dispose(); - (hoveredDeletablePillar.current.geometry).dispose(); - floorGroup.current.remove(hoveredDeletablePillar.current); - echo.success("Pillar Removed!"); - hoveredDeletablePillar.current = undefined; - } -} - -export default DeletePillar; diff --git a/app/src/modules/builder/geomentries/pillars/updateReferencePolesheight.ts b/app/src/modules/builder/geomentries/pillars/updateReferencePolesheight.ts deleted file mode 100644 index f538546..0000000 --- a/app/src/modules/builder/geomentries/pillars/updateReferencePolesheight.ts +++ /dev/null @@ -1,40 +0,0 @@ -import * as THREE from 'three'; - -import * as Types from "../../../../types/world/worldTypes"; - -function updateReferencePolesheight( - intersectionPoint: Types.Vector3, - distance: Types.Number, - referencePole: Types.RefMesh, - floorGroup: Types.RefGroup -): void { - - ////////// Add a Reference Pillar and update its position and scale based on the pointer interaction ////////// - - if (referencePole.current) { - (referencePole.current.material).dispose(); - (referencePole.current.geometry).dispose(); - floorGroup.current.remove(referencePole.current); - referencePole.current.geometry.dispose(); - } - - const shape = new THREE.Shape(); - shape.moveTo(0.5, 0); - shape.absarc(0, 0, 0.5, 0, 2 * Math.PI, false); - - const extrudeSettings = { - depth: distance, - bevelEnabled: false, - }; - - const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); - const material = new THREE.MeshBasicMaterial({ color: "green", transparent: true, opacity: 0.5 }); - referencePole.current = new THREE.Mesh(geometry, material); - referencePole.current.rotateX(Math.PI / 2); - referencePole.current.position.set(intersectionPoint.x, intersectionPoint.y - 0.01, intersectionPoint.z); - referencePole.current.userData = { geometry: geometry, distance: distance, position: { x: intersectionPoint.x, y: intersectionPoint.y - 0.01, z: intersectionPoint.z } }; - - floorGroup.current.add(referencePole.current); -} - -export default updateReferencePolesheight; diff --git a/app/src/modules/builder/geomentries/points/addPointToScene.ts b/app/src/modules/builder/geomentries/points/addPointToScene.ts deleted file mode 100644 index b182128..0000000 --- a/app/src/modules/builder/geomentries/points/addPointToScene.ts +++ /dev/null @@ -1,65 +0,0 @@ -import * as THREE from 'three'; -import * as CONSTANTS from '../../../../types/world/worldConstants'; -import * as Types from "../../../../types/world/worldTypes"; - -function addPointToScene( - position: Types.Vector3, - colour: Types.Color, - currentLayerPoint: Types.RefMeshArray, - floorPlanGroupPoint: Types.RefGroup, - dragPointControls: Types.RefDragControl | undefined, - uuid: Types.RefString | undefined, - Type: Types.String -): Types.Mesh { - - ////////// A function that creates and adds a cube (point) with an outline based on the position and colour given as params, It also updates the drag controls objects and sets the box uuid in uuid.current ////////// - - const geometry = new THREE.BoxGeometry(...CONSTANTS.pointConfig.boxScale); - const material = new THREE.ShaderMaterial({ - uniforms: { - 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: ` - varying vec2 vUv; - - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); - } - `, - fragmentShader: ` - varying vec2 vUv; - uniform vec3 uOuterColor; - uniform vec3 uInnerColor; - - void main() { - // Define the size of the white square as a proportion of the face - float borderThickness = 0.2; // Adjust this value for border thickness - if (vUv.x > borderThickness && vUv.x < 1.0 - borderThickness && - vUv.y > borderThickness && vUv.y < 1.0 - borderThickness) { - gl_FragColor = vec4(uInnerColor, 1.0); // White inner square - } else { - gl_FragColor = vec4(uOuterColor, 1.0); // Blue border - } - } - `, - }); - const point = new THREE.Mesh(geometry, material); - point.name = "point"; - point.userData = { type: Type, color: colour }; - point.position.set(position.x, 0.01, position.z); - - currentLayerPoint.current.push(point); - floorPlanGroupPoint.current.add(point); - if (uuid) { - uuid.current = point.uuid; - } - if (dragPointControls) { - dragPointControls.current!.objects = currentLayerPoint.current; - } - - return point; -} - -export default addPointToScene; diff --git a/app/src/modules/builder/geomentries/points/deletePoint.ts b/app/src/modules/builder/geomentries/points/deletePoint.ts deleted file mode 100644 index 9a58eb5..0000000 --- a/app/src/modules/builder/geomentries/points/deletePoint.ts +++ /dev/null @@ -1,72 +0,0 @@ -import * as Types from "../../../../types/world/worldTypes"; - -import { toast } from "react-toastify"; - -import RemoveConnectedLines from "../lines/removeConnectedLines"; -// import { deletePointApi } from "../../../../services/factoryBuilder/lines/deletePointApi"; -import { Socket } from "socket.io-client"; -import { getUserData } from "../../../../functions/getUserData"; - -function deletePoint( - hoveredDeletablePoint: Types.RefMesh, - onlyFloorlines: Types.RefOnlyFloorLines, - floorPlanGroupPoint: Types.RefGroup, - floorPlanGroupLine: Types.RefGroup, - lines: Types.RefLines, - setDeletedLines: any, - socket: Socket, - projectId?: string, - versionId?: string, -): void { - ////////// Deleting a Point and the lines that are connected to it ////////// - - if (!hoveredDeletablePoint.current) { - return; - } - const { userId, organization, email } = getUserData(); - (hoveredDeletablePoint.current.material).dispose(); - (hoveredDeletablePoint.current.geometry).dispose(); - floorPlanGroupPoint.current.remove(hoveredDeletablePoint.current); - const DeletedPointUUID = hoveredDeletablePoint.current.uuid; - - //REST - - // deletePointApi(organization, DeletedPointUUID); - - //SOCKET - - const data = { - organization, - uuid: DeletedPointUUID, - socketId: socket.id, - versionId, - projectId, - userId, - }; - - // console.log('data: ', data); - socket.emit("v1:Line:delete:point", data); - - ////////// Update onlyFloorlines.current to remove references to the deleted point ////////// - - onlyFloorlines.current = onlyFloorlines.current - .map((floorline) => - floorline.filter( - (line) => - line[0][1] !== DeletedPointUUID && line[1][1] !== DeletedPointUUID - ) - ) - .filter((floorline) => floorline.length > 0); - - RemoveConnectedLines( - DeletedPointUUID, - floorPlanGroupLine, - floorPlanGroupPoint, - setDeletedLines, - lines - ); - - echo.success("Point Removed!"); -} - -export default deletePoint; diff --git a/app/src/modules/builder/geomentries/points/dragPoint.ts b/app/src/modules/builder/geomentries/points/dragPoint.ts deleted file mode 100644 index 6494721..0000000 --- a/app/src/modules/builder/geomentries/points/dragPoint.ts +++ /dev/null @@ -1,44 +0,0 @@ -import * as THREE from "three"; -import * as Types from "../../../../types/world/worldTypes" -import * as CONSTANTS from '../../../../types/world/worldConstants'; - -import updateLinesPositions from "../lines/updateLinesPositions"; -import updateLines from "../lines/updateLines"; -import updateDistanceText from "../lines/updateDistanceText"; -import updateFloorLines from "../floors/updateFloorLines"; - -function DragPoint( - event: Types.IntersectionEvent, - floorPlanGroupPoint: Types.RefGroup, - floorPlanGroupLine: Types.RefGroup, - scene: THREE.Scene, - lines: Types.RefLines, - onlyFloorlines: Types.RefOnlyFloorLines -): void { - - ////////// Calling the line updation of the affected lines and Snapping of the point during the drag ////////// - - const snapThreshold = CONSTANTS.pointConfig.snappingThreshold; - const DragedPoint = event.object as Types.Mesh; - - floorPlanGroupPoint.current.children.forEach((point) => { - let canSnap = - ((DragedPoint.userData.type === CONSTANTS.lineConfig.wallName) && (point.userData.type === CONSTANTS.lineConfig.wallName || point.userData.type === CONSTANTS.lineConfig.floorName)) || - ((DragedPoint.userData.type === CONSTANTS.lineConfig.floorName) && (point.userData.type === CONSTANTS.lineConfig.wallName || point.userData.type === CONSTANTS.lineConfig.floorName)) || - ((DragedPoint.userData.type === CONSTANTS.lineConfig.aisleName) && point.userData.type === CONSTANTS.lineConfig.aisleName); - if (canSnap && point.uuid !== DragedPoint.uuid && point.visible) { - const distance = DragedPoint.position.distanceTo(point.position); - if (distance < snapThreshold) { - DragedPoint.position.copy(point.position); - } - } - }); - - const affectedLines = updateLinesPositions(DragedPoint, lines); - - updateLines(floorPlanGroupLine, affectedLines); - updateDistanceText(scene, floorPlanGroupLine, affectedLines); - updateFloorLines(onlyFloorlines, DragedPoint); -} - -export default DragPoint; \ No newline at end of file diff --git a/app/src/modules/builder/geomentries/points/removeSoloPoint.ts b/app/src/modules/builder/geomentries/points/removeSoloPoint.ts deleted file mode 100644 index 56d3ee7..0000000 --- a/app/src/modules/builder/geomentries/points/removeSoloPoint.ts +++ /dev/null @@ -1,37 +0,0 @@ -import * as Types from "../../../../types/world/worldTypes"; - -function removeSoloPoint( - line: Types.RefLine, - floorPlanGroupLine: Types.RefGroup, - floorPlanGroupPoint: Types.RefGroup -): void { - - ////////// Remove the point if there is only one point and if it is not connected to any other line and also the reference line ////////// - - if (line.current[0]) { - const pointUUID = line.current[0][1]; - let isConnected = false; - - floorPlanGroupLine.current.children.forEach((line) => { - 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); - } - }); - } - line.current = []; - } -} - -export default removeSoloPoint; diff --git a/app/src/modules/builder/geomentries/roofs/addRoofToScene.ts b/app/src/modules/builder/geomentries/roofs/addRoofToScene.ts deleted file mode 100644 index 4630efc..0000000 --- a/app/src/modules/builder/geomentries/roofs/addRoofToScene.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as THREE from 'three'; -import * as CONSTANTS from '../../../../types/world/worldConstants'; -import * as Types from "../../../../types/world/worldTypes"; - -function addRoofToScene( - shape: Types.Shape, - floor: Types.Number, - userData: Types.UserData, - floorGroup: Types.RefGroup -): void { - - ////////// Creating a Polygon roof from the shape of the Polygon floor ////////// - - const extrudeSettings: THREE.ExtrudeGeometryOptions = { - depth: CONSTANTS.roofConfig.height, - bevelEnabled: false - }; - - const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); - const material = new THREE.MeshStandardMaterial({ color: CONSTANTS.roofConfig.defaultColor, side: THREE.DoubleSide }); - const mesh = new THREE.Mesh(geometry, material); - mesh.position.y = CONSTANTS.wallConfig.height + floor; - mesh.castShadow = true; - mesh.receiveShadow = true; - mesh.rotateX(Math.PI / 2); - mesh.userData.uuids = userData; - mesh.name = `Roof_Layer_${(floor / CONSTANTS.wallConfig.height) + 1}`; - - floorGroup.current.add(mesh); -} - -export default addRoofToScene; diff --git a/app/src/modules/builder/geomentries/roofs/hideRoof.ts b/app/src/modules/builder/geomentries/roofs/hideRoof.ts deleted file mode 100644 index 8eb80f8..0000000 --- a/app/src/modules/builder/geomentries/roofs/hideRoof.ts +++ /dev/null @@ -1,47 +0,0 @@ -import * as THREE from 'three'; - -import * as Types from "../../../../types/world/worldTypes"; - -function hideRoof( - visibility: Types.Boolean, - floorGroup: Types.RefGroup, - camera: THREE.Camera -): void { - - ////////// Toggles the visibility of the roof based on the camera position and the Roof visibility button on UI ////////// - - const v = new THREE.Vector3(); - const u = new THREE.Vector3(); - - if (visibility === true && floorGroup.current) { - for (const child of floorGroup.current.children) { - if (child.name.includes("Roof")) { - const roofChild = child as Types.Mesh; - roofChild.getWorldDirection(v); - camera?.getWorldDirection(u); - if (roofChild.material) { - const materials = Array.isArray(roofChild.material) ? roofChild.material : [roofChild.material]; - materials.forEach(material => { - material.visible = v.dot(u) < 0.25; - }); - } - } - } - } else { - if (floorGroup.current) { - for (const child of floorGroup.current.children) { - if (child.name.includes("Roof")) { - const roofChild = child as Types.Mesh; - if (roofChild.material) { - const materials = Array.isArray(roofChild.material) ? roofChild.material : [roofChild.material]; - materials.forEach(material => { - material.visible = false; - }); - } - } - } - } - } -} - -export default hideRoof; diff --git a/app/src/modules/builder/geomentries/walls/addWallItems.ts b/app/src/modules/builder/geomentries/walls/addWallItems.ts deleted file mode 100644 index 809b093..0000000 --- a/app/src/modules/builder/geomentries/walls/addWallItems.ts +++ /dev/null @@ -1,164 +0,0 @@ -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 { Socket } from "socket.io-client"; -import { retrieveGLTF, storeGLTF } from "../../../../utils/indexDB/idbUtils"; -import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; -import { getUserData } from "../../../../functions/getUserData"; - -async function AddWallItems( - selected: any, - raycaster: THREE.Raycaster, - CSGGroup: Types.RefMesh, - setWallItems: Types.setWallItemSetState, - socket: Socket, - projectId?: string, - versionId?: string -): Promise { - - const { userId, organization } = getUserData(); - 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) 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, - assetId: 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 data = { - organization, - modelUuid: model.uuid, - modelName: newWallItem.modelName, - assetId: selected.id, - type: selected.subCategory, - csgposition: newWallItem.csgposition, - csgscale: newWallItem.csgscale, - position: newWallItem.position, - quaternion: newWallItem.quaternion, - scale: newWallItem.scale, - socketId: socket.id, - versionId, - projectId, - userId, - }; - - 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)); - echo.success("Model Added!"); - return updatedItems; - }); - } -} - -export default AddWallItems; diff --git a/app/src/modules/builder/geomentries/walls/deleteWallItems.ts b/app/src/modules/builder/geomentries/walls/deleteWallItems.ts deleted file mode 100644 index 5d16e91..0000000 --- a/app/src/modules/builder/geomentries/walls/deleteWallItems.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { getUserData } from "../../../../functions/getUserData"; -import * as Types from "../../../../types/world/worldTypes"; -// import { deleteWallItem } from '../../../../services/factoryBuilder/assest/wallAsset/deleteWallItemApi'; -import { Socket } from "socket.io-client"; - -function DeleteWallItems( - hoveredDeletableWallItem: Types.RefMesh, - setWallItems: Types.setWallItemSetState, - wallItems: Types.wallItems, - socket: Socket, - projectId?: string, - versionId? : string, -): void { - ////////// Deleting the hovered Wall GLTF from thewallItems and also update it in the localstorage ////////// - const { userId, organization, email } = getUserData(); - if (hoveredDeletableWallItem.current && hoveredDeletableWallItem.current) { - setWallItems([]); - let WallItemsRef = wallItems; - 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; - setWallItems(WallItemsRef); - - //REST - - // await deleteWallItem(organization, removedItem?.model?.uuid!, removedItem?.modelName!) - - //SOCKET - - const data = { - organization, - modelUuid: removedItem?.model?.uuid!, - modelName: removedItem?.modelName!, - socketId: socket.id, - projectId, - versionId, - userId, - }; - - socket.emit("v1:wallItems:delete", data); - - const WallItemsForStorage = WallItemsRef.map((item) => { - const { model, ...rest } = item; - return { - ...rest, - modelUuid: model?.uuid, - }; - }); - - localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); - hoveredDeletableWallItem.current = null; - }, 50); - } -} - -export default DeleteWallItems; diff --git a/app/src/modules/builder/geomentries/walls/hideWalls.ts b/app/src/modules/builder/geomentries/walls/hideWalls.ts deleted file mode 100644 index 02233b0..0000000 --- a/app/src/modules/builder/geomentries/walls/hideWalls.ts +++ /dev/null @@ -1,45 +0,0 @@ -import * as THREE from 'three'; -import * as Types from "../../../../types/world/worldTypes"; - -function hideWalls( - visibility: Types.Boolean, - scene: THREE.Scene, - camera: THREE.Camera -): void { - const wallNormal = new THREE.Vector3(); - const cameraToWall = new THREE.Vector3(); - const cameraDirection = new THREE.Vector3(); - - if (visibility === true) { - for (const children of scene.children) { - if (children.name === "Walls" && children.children[0]?.children.length > 0) { - children.children[0].children.forEach((child: any) => { - if (child.children[0]?.userData.WallType === "RoomWall") { - const wallMesh = child.children[0]; - wallMesh.getWorldDirection(wallNormal); - cameraToWall.copy(wallMesh.position).sub(camera.position).normalize(); - camera.getWorldDirection(cameraDirection); - const isFacingCamera = wallNormal.dot(cameraToWall) > 0; - const isInFrontOfCamera = cameraDirection.dot(cameraToWall) > -0.3; - - if (wallMesh.material) { - wallMesh.material.visible = isFacingCamera && isInFrontOfCamera; - } - } - }); - } - } - } else { - for (const children of scene.children) { - if (children.name === "Walls" && children.children[0]?.children.length > 0) { - children.children[0].children.forEach((child: any) => { - if (child.children[0]?.userData.WallType === "RoomWall" && child.children[0].material) { - child.children[0].material.visible = true; - } - }); - } - } - } -} - -export default hideWalls; \ No newline at end of file diff --git a/app/src/modules/builder/geomentries/walls/loadWalls.ts b/app/src/modules/builder/geomentries/walls/loadWalls.ts deleted file mode 100644 index 14d72c4..0000000 --- a/app/src/modules/builder/geomentries/walls/loadWalls.ts +++ /dev/null @@ -1,140 +0,0 @@ -import * as THREE from 'three'; -import * as turf from '@turf/turf'; -import * as CONSTANTS from '../../../../types/world/worldConstants'; - -import * as Types from "../../../../types/world/worldTypes"; -import getRoomsFromLines from '../lines/getRoomsFromLines'; - -async function loadWalls( - lines: Types.RefLines, - setWalls: any, -): Promise { - ////////// Removes the old walls if any, Checks if there is any overlapping in lines if any updates it , starts function that creates floor and roof ////////// - - const Walls: Types.Walls = []; - const Rooms: Types.Rooms = []; - - localStorage.setItem("Lines", JSON.stringify(lines.current)); - - if (lines.current.length > 1) { - - ////////// Add Walls that are forming a room ////////// - - const wallSet = new Set(); - - const rooms: Types.Rooms = await getRoomsFromLines(lines); - Rooms.push(...rooms); - - Rooms.forEach(({ coordinates: room, layer }) => { - for (let i = 0; i < room.length - 1; i++) { - const uuid1 = room[i].uuid; - const uuid2 = room[(i + 1) % room.length].uuid; - const wallId = `${uuid1}_${uuid2}`; - - if (!wallSet.has(wallId)) { - const p1 = room[i].position; - const p2 = room[(i + 1) % room.length].position; - - const shape = new THREE.Shape(); - shape.moveTo(0, 0); - shape.lineTo(0, CONSTANTS.wallConfig.height); - shape.lineTo(p2.distanceTo(p1), CONSTANTS.wallConfig.height); - shape.lineTo(p2.distanceTo(p1), 0); - shape.lineTo(0, 0); - - const extrudeSettings = { - depth: CONSTANTS.wallConfig.width, - bevelEnabled: false - }; - const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); - const angle = Math.atan2(p2.z - p1.z, p2.x - p1.x); - Walls.push([geometry, [0, -angle, 0], [p1.x, (layer - 1) * CONSTANTS.wallConfig.height, p1.z], "RoomWall", layer]); - - wallSet.add(wallId); - } - } - }); - - ////////// Add Walls that are not forming any room ////////// - - lines.current.forEach(line => { - if (line[0][3] && line[1][3] !== CONSTANTS.lineConfig.wallName) { - return; - } - const [uuid1, uuid2] = line.map(point => point[1]); - let isInRoom = false; - const lineLayer = line[0][2]; - - for (let room of Rooms) { - const roomLayer = room.layer; - if (roomLayer !== lineLayer) continue; - for (let i = 0; i < room.coordinates.length - 1; i++) { - const roomUuid1 = room.coordinates[i].uuid; - const roomUuid2 = room.coordinates[(i + 1) % room.coordinates.length].uuid; - if ( - (uuid1 === roomUuid1 && uuid2 === roomUuid2) || - (uuid1 === roomUuid2 && uuid2 === roomUuid1) - ) { - isInRoom = true; - break; - } - } - if (isInRoom) break; - } - - if (!isInRoom) { - const p1 = new THREE.Vector3(line[0][0].x, 0, line[0][0].z); - const p2 = new THREE.Vector3(line[1][0].x, 0, line[1][0].z); - - let isCollinear = false; - for (let room of Rooms) { - if (room.layer !== lineLayer) continue; - for (let i = 0; i < room.coordinates.length - 1; i++) { - const roomP1 = room.coordinates[i].position; - const roomP2 = room.coordinates[(i + 1) % room.coordinates.length].position; - const lineFeature = turf.lineString([[p1.x, p1.z], [p2.x, p2.z]]); - const roomFeature = turf.lineString([[roomP1.x, roomP1.z], [roomP2.x, roomP2.z]]); - if (turf.booleanOverlap(lineFeature, roomFeature)) { - isCollinear = true; - break; - } - } - if (isCollinear) break; - } - - if (!isCollinear) { - const shape = new THREE.Shape(); - shape.moveTo(0, 0); - shape.lineTo(0, CONSTANTS.wallConfig.height); - shape.lineTo(p2.distanceTo(p1), CONSTANTS.wallConfig.height); - shape.lineTo(p2.distanceTo(p1), 0); - shape.lineTo(0, 0); - - const extrudeSettings = { - depth: CONSTANTS.wallConfig.width, - bevelEnabled: false - }; - const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); - const angle = Math.atan2(p2.z - p1.z, p2.x - p1.x); - Walls.push([geometry, [0, -angle, 0], [p1.x, (lineLayer - 1) * CONSTANTS.wallConfig.height, p1.z], "SegmentWall", lineLayer]); - } - } - }); - setWalls(Walls); - } else { - setWalls([]); - } -} - -export default loadWalls; - - -// A----- B----- C -// | | | -// | | | -// | | | -// F----- E----- D - -// 1. A -> B, B -> C, C -> D, D -> E, E -> F, F -> A, B -> E - -// 2. E -> F, F -> A, A -> B, B -> E, E -> D, D -> C, C -> B \ No newline at end of file diff --git a/app/src/modules/builder/geomentries/zones/addZonesToScene.ts b/app/src/modules/builder/geomentries/zones/addZonesToScene.ts deleted file mode 100644 index 2832c7a..0000000 --- a/app/src/modules/builder/geomentries/zones/addZonesToScene.ts +++ /dev/null @@ -1,50 +0,0 @@ -import * as THREE from 'three'; -import * as Types from '../../../../types/world/worldTypes'; -import * as CONSTANTS from '../../../../types/world/worldConstants'; - -const baseMaterial = new THREE.ShaderMaterial({ - side: THREE.DoubleSide, - vertexShader: ` - varying vec2 vUv; - void main(){ - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); - vUv = uv; - } - `, - fragmentShader: ` - varying vec2 vUv; - uniform vec3 uOuterColor; - void main(){ - float alpha = 1.0 - vUv.y; - gl_FragColor = vec4(uOuterColor, alpha); - } - `, - uniforms: { - uOuterColor: { value: new THREE.Color(CONSTANTS.zoneConfig.defaultColor) }, - }, - transparent: true, -}); - -export default function addZonesToScene( - line: Types.Line, - floorGroupZone: Types.RefGroup, - color: THREE.Color -) { - const point1 = line[0][0]; - const point2 = line[1][0]; - - const length = (new THREE.Vector3(point2.x, point2.y, point2.z)).distanceTo(new THREE.Vector3(point1.x, point1.y, point1.z)); - const angle = Math.atan2(point2.z - point1.z, point2.x - point1.x); - - const geometry = new THREE.PlaneGeometry(length, 10); - - const material = baseMaterial.clone(); - material.uniforms.uOuterColor.value.set(color.r, color.g, color.b); - - const mesh = new THREE.Mesh(geometry, material); - - mesh.position.set((point1.x + point2.x) / 2, ((line[0][2] - 1) * CONSTANTS.wallConfig.height) + 5, (point1.z + point2.z) / 2); - mesh.rotation.y = -angle; - - floorGroupZone.current.add(mesh); -} diff --git a/app/src/modules/builder/geomentries/zones/loadZones.ts b/app/src/modules/builder/geomentries/zones/loadZones.ts deleted file mode 100644 index aa78199..0000000 --- a/app/src/modules/builder/geomentries/zones/loadZones.ts +++ /dev/null @@ -1,19 +0,0 @@ -import * as Types from '../../../../types/world/worldTypes'; -import * as THREE from 'three'; -import * as CONSTANTS from '../../../../types/world/worldConstants'; -import addZonesToScene from './addZonesToScene'; - -export default function loadZones( - lines: Types.RefLines, - floorGroupZone: Types.RefGroup -) { - if (!floorGroupZone.current) return - floorGroupZone.current.children = []; - const zones = lines.current.filter((line) => line[0][3] && line[1][3] === CONSTANTS.lineConfig.zoneName); - - if (zones.length > 0) { - zones.forEach((zone: Types.Line) => { - addZonesToScene(zone, floorGroupZone, new THREE.Color(CONSTANTS.zoneConfig.color)) - }) - } -} \ No newline at end of file diff --git a/app/src/modules/builder/groups/floorGroup.tsx b/app/src/modules/builder/groups/floorGroup.tsx deleted file mode 100644 index 4af21b9..0000000 --- a/app/src/modules/builder/groups/floorGroup.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { useFrame, useThree } from "@react-three/fiber"; -import { - useAddAction, - useRoofVisibility, - useToggleView, - useWallVisibility, - useUpdateScene, - useRenameModeStore, - useToolMode, -} from "../../../store/builder/store"; -import hideRoof from "../geomentries/roofs/hideRoof"; -import hideWalls from "../geomentries/walls/hideWalls"; -import addAndUpdateReferencePillar from "../geomentries/pillars/addAndUpdateReferencePillar"; -import { useEffect } from "react"; -import addPillar from "../geomentries/pillars/addPillar"; -import DeletePillar from "../geomentries/pillars/deletePillar"; -import DeletableHoveredPillar from "../geomentries/pillars/deletableHoveredPillar"; -import loadFloor from "../geomentries/floors/loadFloor"; -import { useLeftData, useTopData } from "../../../store/visualization/useZone3DWidgetStore"; - -const FloorGroup = ({ - floorGroup, - lines, - referencePole, - hoveredDeletablePillar, -}: any) => { - const state = useThree(); - const { roofVisibility } = useRoofVisibility(); - const { wallVisibility } = useWallVisibility(); - const { toggleView } = useToggleView(); - const { scene, camera, raycaster, gl } = useThree(); - const { addAction } = useAddAction(); - const { toolMode } = useToolMode(); - const { updateScene, setUpdateScene } = useUpdateScene(); - const { setTop } = useTopData(); - const { setLeft } = useLeftData(); - const { isRenameMode, setIsRenameMode } = useRenameModeStore(); - - useEffect(() => { - if (updateScene) { - loadFloor(lines, floorGroup); - setUpdateScene(false); - } - }, [updateScene]); - - useEffect(() => { - if (!addAction) { - if (referencePole.current) { - (referencePole.current as any).material.dispose(); - (referencePole.current.geometry as any).dispose(); - floorGroup.current.remove(referencePole.current); - referencePole.current = undefined; - } - } - }, [addAction]); - - 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) => { - setIsRenameMode(false); - if (evt.button === 0) { - isLeftMouseDown = false; - if (!drag) { - if (addAction === "pillar") { - addPillar(referencePole, floorGroup); - } - if (toolMode === '3D-Delete') { - DeletePillar(hoveredDeletablePillar, floorGroup); - } - } - } - }; - - const onMouseMove = (evt: any) => { - if (!canvasElement) return; - const canvasRect = canvasElement.getBoundingClientRect(); - const relativeX = evt.clientX - canvasRect.left; - const relativeY = evt.clientY - canvasRect.top; - // if (!isRenameMode) { - // setTop(relativeY); - // setLeft(relativeX); - // } - if (isLeftMouseDown) { - drag = true; - } - }; - - canvasElement.addEventListener("mousedown", onMouseDown); - canvasElement.addEventListener("mouseup", onMouseUp); - canvasElement.addEventListener("mousemove", onMouseMove); - - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - }; - }, [toolMode, addAction, isRenameMode]); - - useFrame(() => { - hideRoof(roofVisibility, floorGroup, camera); - hideWalls(wallVisibility, scene, camera); - - if (addAction === "pillar") { - addAndUpdateReferencePillar(raycaster, floorGroup, referencePole); - } - if (toolMode === '3D-Delete') { - DeletableHoveredPillar(state, floorGroup, hoveredDeletablePillar); - } - }); - - return ( - - ); -}; - -export default FloorGroup; diff --git a/app/src/modules/builder/groups/floorPlanGroup.tsx b/app/src/modules/builder/groups/floorPlanGroup.tsx deleted file mode 100644 index e35cec9..0000000 --- a/app/src/modules/builder/groups/floorPlanGroup.tsx +++ /dev/null @@ -1,193 +0,0 @@ -import { useEffect } from "react"; -import * as Types from '../../../types/world/worldTypes'; -import { useActiveLayer, useDeletedLines, useToolMode, useNewLines, useRemovedLayer, useSocketStore, useToggleView, useUpdateScene } from "../../../store/builder/store"; -import Layer2DVisibility from "../geomentries/layers/layer2DVisibility"; -import { useFrame, useThree } from "@react-three/fiber"; -import DeletableLineorPoint from "../functions/deletableLineOrPoint"; -import removeSoloPoint from "../geomentries/points/removeSoloPoint"; -import removeReferenceLine from "../geomentries/lines/removeReferenceLine"; -import DeleteLayer from "../geomentries/layers/deleteLayer"; -import { getLines } from "../../../services/factoryBuilder/lines/getLinesApi"; -import objectLinesToArray from "../geomentries/lines/lineConvertions/objectLinesToArray"; -import loadInitialPoint from "../IntialLoad/loadInitialPoint"; -import loadInitialLine from "../IntialLoad/loadInitialLine"; -import deletePoint from "../geomentries/points/deletePoint"; -import deleteLine from "../geomentries/lines/deleteLine"; -import drawWall from "../geomentries/lines/drawWall"; -import drawOnlyFloor from "../geomentries/floors/drawOnlyFloor"; -import addDragControl from "../eventDeclaration/dragControlDeclaration"; -import { useParams } from "react-router-dom"; -import { getUserData } from "../../../functions/getUserData"; -import { useVersionContext } from "../version/versionContext"; - -const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoint, floorGroup, currentLayerPoint, dragPointControls, hoveredDeletablePoint, hoveredDeletableLine, plane, line, lines, onlyFloorline, onlyFloorlines, ReferenceLineMesh, LineCreated, isSnapped, ispreSnapped, snappedPoint, isSnappedUUID, isAngleSnapped, anglesnappedPoint }: any) => { - const state = useThree(); - const { camera, gl, raycaster, controls } = state; - const { activeLayer } = useActiveLayer(); - const { toggleView } = useToggleView(); - const { toolMode } = useToolMode(); - const { removedLayer, setRemovedLayer } = useRemovedLayer(); - const { setUpdateScene } = useUpdateScene(); - const { setNewLines } = useNewLines(); - const { setDeletedLines } = useDeletedLines(); - const { socket } = useSocketStore(); - const { ydoc } = useSocketStore(); - const { selectedVersionStore } = useVersionContext(); - const { selectedVersion } = selectedVersionStore(); - const { projectId } = useParams(); - const { organization } = getUserData(); - - - useEffect(() => { - if (toolMode === 'move') { - addDragControl(dragPointControls, currentLayerPoint, state, floorPlanGroupPoint, floorPlanGroupLine, lines, onlyFloorlines, socket, projectId, selectedVersion?.versionId || '',); - } - - return () => { - if (dragPointControls.current) { - dragPointControls.current.enabled = false; - } - }; - }, [toolMode, state]); - - useEffect(() => { - if (!selectedVersion) return; - - getLines(organization, projectId, selectedVersion?.versionId || '').then((data) => { - - const Lines: Types.Lines = objectLinesToArray(data); - - if (Lines) { - lines.current = Lines; - loadInitialPoint(lines, floorPlanGroupPoint, currentLayerPoint, dragPointControls); - loadInitialLine(floorPlanGroupLine, lines); - setUpdateScene(true); - } - }) - }, [selectedVersion?.versionId]); - - useEffect(() => { - if (!toggleView) { - removeSoloPoint(line, floorPlanGroupLine, floorPlanGroupPoint); - removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line); - } - }, [toggleView]); - - useEffect(() => { - removeSoloPoint(line, floorPlanGroupLine, floorPlanGroupPoint); - removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line); - }, [toolMode]); - - useEffect(() => { - if (toolMode === 'move' && toggleView) { - if (dragPointControls.current) { - dragPointControls.current.enabled = true; - } - } else { - if (dragPointControls.current) { - dragPointControls.current.enabled = false; - } - } - }, [toolMode, toggleView, state]); - - useEffect(() => { - Layer2DVisibility(activeLayer, floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoint, currentLayerPoint, dragPointControls); - }, [activeLayer]); - - useEffect(() => { - if (removedLayer !== null) { - DeleteLayer(removedLayer, lines, floorPlanGroupLine, floorPlanGroupPoint, onlyFloorlines, floorGroup, setDeletedLines, setRemovedLayer, socket, projectId, selectedVersion?.versionId || '',); - } - }, [removedLayer]); - - 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; - } - if (controls) { - (controls as any).enabled = true; - } - } - - const onMouseMove = () => { - if (isLeftMouseDown) { - drag = true; - } - }; - - const onContextMenu = (e: any) => { - e.preventDefault(); - if (toolMode === "Wall" || toolMode === "Floor") { - removeSoloPoint(line, floorPlanGroupLine, floorPlanGroupPoint); - removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line); - } - }; - - const onMouseClick = (evt: any) => { - if (!plane.current || drag) return; - - if (toolMode === "2D-Delete") { - if (hoveredDeletablePoint.current !== null) { - deletePoint(hoveredDeletablePoint, onlyFloorlines, floorPlanGroupPoint, floorPlanGroupLine, lines, setDeletedLines, socket, projectId, selectedVersion?.versionId || '',); - } - if (hoveredDeletableLine.current !== null) { - deleteLine(hoveredDeletableLine, onlyFloorlines, lines, floorPlanGroupLine, floorPlanGroupPoint, setDeletedLines, socket, projectId, selectedVersion?.versionId || '',); - } - } - - if (toolMode === "Wall") { - drawWall(raycaster, plane, floorPlanGroupPoint, snappedPoint, isSnapped, isSnappedUUID, line, ispreSnapped, anglesnappedPoint, isAngleSnapped, lines, floorPlanGroupLine, floorPlanGroup, ReferenceLineMesh, LineCreated, currentLayerPoint, dragPointControls, setNewLines, setDeletedLines, activeLayer, socket, projectId, selectedVersion?.versionId || '', ydoc); - } - - if (toolMode === "Floor") { - drawOnlyFloor(raycaster, state, camera, plane, floorPlanGroupPoint, snappedPoint, isSnapped, isSnappedUUID, line, ispreSnapped, anglesnappedPoint, isAngleSnapped, onlyFloorline, onlyFloorlines, lines, floorPlanGroupLine, floorPlanGroup, ReferenceLineMesh, LineCreated, currentLayerPoint, dragPointControls, setNewLines, setDeletedLines, activeLayer, socket, projectId, selectedVersion?.versionId || '',); - } - } - - if (toolMode === "2D-Delete" || toolMode === "Wall" || toolMode === "Floor") { - canvasElement.addEventListener("mousedown", onMouseDown); - canvasElement.addEventListener("mouseup", onMouseUp); - canvasElement.addEventListener("mousemove", onMouseMove); - canvasElement.addEventListener("click", onMouseClick); - canvasElement.addEventListener("contextmenu", onContextMenu); - } - - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - canvasElement.removeEventListener("click", onMouseClick); - canvasElement.removeEventListener("contextmenu", onContextMenu); - }; - }, [toolMode, activeLayer]) - - - useFrame(() => { - if (toolMode === '2D-Delete') { - DeletableLineorPoint(state, plane, floorPlanGroupLine, floorPlanGroupPoint, hoveredDeletableLine, hoveredDeletablePoint); - } - }) - - return ( - - - - - ) -} - -export default FloorPlanGroup; \ No newline at end of file diff --git a/app/src/modules/builder/groups/wallItemsGroup.tsx b/app/src/modules/builder/groups/wallItemsGroup.tsx index 210377b..28f4f4b 100644 --- a/app/src/modules/builder/groups/wallItemsGroup.tsx +++ b/app/src/modules/builder/groups/wallItemsGroup.tsx @@ -1,291 +1,291 @@ -import { useEffect } from "react"; -import { - useObjectPosition, - useObjectRotation, - useSelectedWallItem, - useSocketStore, - useWallItems, - useSelectedItem, - useToolMode, -} from "../../../store/builder/store"; -import { Csg } from "../csg/csg"; -import * as Types from "../../../types/world/worldTypes"; -import * as CONSTANTS from "../../../types/world/worldConstants"; -import * as THREE from "three"; -import { useThree } from "@react-three/fiber"; -import handleMeshMissed from "../eventFunctions/handleMeshMissed"; -import DeleteWallItems from "../geomentries/walls/deleteWallItems"; -import loadInitialWallItems from "../IntialLoad/loadInitialWallItems"; -import AddWallItems from "../geomentries/walls/addWallItems"; -import useModuleStore from "../../../store/useModuleStore"; -import { useParams } from "react-router-dom"; -import { getUserData } from "../../../functions/getUserData"; -import { useVersionContext } from "../version/versionContext"; +// import { useEffect } from "react"; +// import { +// useObjectPosition, +// useObjectRotation, +// useSelectedWallItem, +// useSocketStore, +// useWallItems, +// useSelectedItem, +// useToolMode, +// } from "../../../store/builder/store"; +// import { Csg } from "../csg/csg"; +// import * as Types from "../../../types/world/worldTypes"; +// import * as CONSTANTS from "../../../types/world/worldConstants"; +// import * as THREE from "three"; +// import { useThree } from "@react-three/fiber"; +// import handleMeshMissed from "../eventFunctions/handleMeshMissed"; +// import DeleteWallItems from "../geomentries/walls/deleteWallItems"; +// import loadInitialWallItems from "../IntialLoad/loadInitialWallItems"; +// import AddWallItems from "../geomentries/walls/addWallItems"; +// import useModuleStore from "../../../store/useModuleStore"; +// import { useParams } from "react-router-dom"; +// import { getUserData } from "../../../functions/getUserData"; +// import { useVersionContext } from "../version/versionContext"; -const WallItemsGroup = ({ - currentWallItem, - hoveredDeletableWallItem, - selectedItemsIndex, - setSelectedItemsIndex, - CSGGroup, -}: any) => { - const state = useThree(); - const { socket } = useSocketStore(); - const { pointer, camera, raycaster } = state; - const { toolMode } = useToolMode(); - const { wallItems, setWallItems } = useWallItems(); - const { setObjectPosition } = useObjectPosition(); - const { setObjectRotation } = useObjectRotation(); - const { setSelectedWallItem } = useSelectedWallItem(); - const { activeModule } = useModuleStore(); - const { selectedItem } = useSelectedItem(); - const { selectedVersionStore } = useVersionContext(); - const { selectedVersion } = selectedVersionStore(); - const { projectId } = useParams(); - const { userId, organization } = getUserData(); +// const WallItemsGroup = ({ +// currentWallItem, +// hoveredDeletableWallItem, +// selectedItemsIndex, +// setSelectedItemsIndex, +// CSGGroup, +// }: any) => { +// const state = useThree(); +// const { socket } = useSocketStore(); +// const { pointer, camera, raycaster } = state; +// const { toolMode } = useToolMode(); +// const { wallItems, setWallItems } = useWallItems(); +// const { setObjectPosition } = useObjectPosition(); +// const { setObjectRotation } = useObjectRotation(); +// const { setSelectedWallItem } = useSelectedWallItem(); +// const { activeModule } = useModuleStore(); +// const { selectedItem } = useSelectedItem(); +// const { selectedVersionStore } = useVersionContext(); +// const { selectedVersion } = selectedVersionStore(); +// const { projectId } = useParams(); +// const { userId, organization } = getUserData(); - useEffect(() => { - // Load Wall Items from the backend - if (!projectId || !selectedVersion) return; - loadInitialWallItems(setWallItems, projectId, selectedVersion?.versionId); - }, [selectedVersion?.versionId]); +// useEffect(() => { +// // Load Wall Items from the backend +// if (!projectId || !selectedVersion) return; +// loadInitialWallItems(setWallItems, projectId, selectedVersion?.versionId); +// }, [selectedVersion?.versionId]); - ////////// Update the Position value changes in the selected item ////////// +// ////////// Update the Position value changes in the selected item ////////// - useEffect(() => { - const canvasElement = state.gl.domElement; - function handlePointerMove(e: any) { - if (selectedItemsIndex !== null && toolMode === 'cursor' && 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")); +// useEffect(() => { +// const canvasElement = state.gl.domElement; +// function handlePointerMove(e: any) { +// if (selectedItemsIndex !== null && toolMode === 'cursor' && 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")); - if (Object) { - (state.controls as any)!.enabled = false; - setWallItems((prevItems: any) => { - const updatedItems = [...prevItems]; - let position: [number, number, number] = [0, 0, 0]; +// if (Object) { +// (state.controls as any)!.enabled = false; +// setWallItems((prevItems: any) => { +// const updatedItems = [...prevItems]; +// let position: [number, number, number] = [0, 0, 0]; - 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") { - position = [Object!.point.x, Object!.point.y, Object!.point.z]; - } +// 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") { +// position = [Object!.point.x, Object!.point.y, Object!.point.z]; +// } - requestAnimationFrame(() => { - setObjectPosition(new THREE.Vector3(...position)); - setObjectRotation({ - x: THREE.MathUtils.radToDeg(Object!.object.rotation.x), - y: THREE.MathUtils.radToDeg(Object!.object.rotation.y), - z: THREE.MathUtils.radToDeg(Object!.object.rotation.z), - }); - }); +// requestAnimationFrame(() => { +// setObjectPosition(new THREE.Vector3(...position)); +// setObjectRotation({ +// x: THREE.MathUtils.radToDeg(Object!.object.rotation.x), +// y: THREE.MathUtils.radToDeg(Object!.object.rotation.y), +// z: THREE.MathUtils.radToDeg(Object!.object.rotation.z), +// }); +// }); - updatedItems[selectedItemsIndex] = { - ...updatedItems[selectedItemsIndex], - position: position, - quaternion: Object!.object.quaternion.clone() as Types.QuaternionType, - }; +// updatedItems[selectedItemsIndex] = { +// ...updatedItems[selectedItemsIndex], +// position: position, +// quaternion: Object!.object.quaternion.clone() as Types.QuaternionType, +// }; - return updatedItems; - }); - } - } - } +// return updatedItems; +// }); +// } +// } +// } - async function handlePointerUp() { - const Raycaster = state.raycaster; - const intersects = Raycaster.intersectObjects( - CSGGroup.current?.children[0].children!, - true - ); - const Object = intersects.find((child) => - child.object.name.includes("WallRaycastReference") - ); - if (Object) { - if (selectedItemsIndex !== null) { - let currentItem: any = null; - setWallItems((prevItems: any) => { - const updatedItems = [...prevItems]; - const WallItemsForStorage = updatedItems.map((item) => { - const { model, ...rest } = item; - return { - ...rest, - modelUuid: model?.uuid, - }; - }); +// async function handlePointerUp() { +// const Raycaster = state.raycaster; +// const intersects = Raycaster.intersectObjects( +// CSGGroup.current?.children[0].children!, +// true +// ); +// const Object = intersects.find((child) => +// child.object.name.includes("WallRaycastReference") +// ); +// if (Object) { +// if (selectedItemsIndex !== null) { +// let currentItem: any = null; +// setWallItems((prevItems: any) => { +// const updatedItems = [...prevItems]; +// const WallItemsForStorage = updatedItems.map((item) => { +// const { model, ...rest } = item; +// return { +// ...rest, +// modelUuid: model?.uuid, +// }; +// }); - currentItem = updatedItems[selectedItemsIndex]; - localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); - return updatedItems; - }); +// currentItem = updatedItems[selectedItemsIndex]; +// localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); +// return updatedItems; +// }); - setTimeout(async () => { +// setTimeout(async () => { - //REST +// //REST - // await setWallItem( - // organization, - // currentItem?.model?.uuid, - // currentItem.modelName, - // currentItem.assetId, - // currentItem.type!, - // currentItem.csgposition!, - // currentItem.csgscale!, - // currentItem.position, - // currentItem.quaternion, - // currentItem.scale!, - // ) +// // await setWallItem( +// // organization, +// // currentItem?.model?.uuid, +// // currentItem.modelName, +// // currentItem.assetId, +// // currentItem.type!, +// // currentItem.csgposition!, +// // currentItem.csgscale!, +// // currentItem.position, +// // currentItem.quaternion, +// // currentItem.scale!, +// // ) - //SOCKET +// //SOCKET - const data = { - organization, - modelUuid: currentItem.model?.uuid!, - assetId: currentItem.assetId, - modelName: currentItem.modelName!, - type: currentItem.type!, - csgposition: currentItem.csgposition!, - csgscale: currentItem.csgscale!, - position: currentItem.position!, - quaternion: currentItem.quaternion, - scale: currentItem.scale!, - socketId: socket.id, - versionId: selectedVersion?.versionId || '', - projectId, - userId - }; +// const data = { +// organization, +// modelUuid: currentItem.model?.uuid!, +// assetId: currentItem.assetId, +// modelName: currentItem.modelName!, +// type: currentItem.type!, +// csgposition: currentItem.csgposition!, +// csgscale: currentItem.csgscale!, +// position: currentItem.position!, +// quaternion: currentItem.quaternion, +// scale: currentItem.scale!, +// socketId: socket.id, +// versionId: selectedVersion?.versionId || '', +// projectId, +// userId +// }; - // console.log('data: ', data); - socket.emit("v1:wallItems:set", data); - }, 0); - (state.controls as any)!.enabled = true; - } - } - } +// // console.log('data: ', data); +// socket.emit("v1:wallItems:set", data); +// }, 0); +// (state.controls as any)!.enabled = true; +// } +// } +// } - canvasElement.addEventListener("pointermove", handlePointerMove); - canvasElement.addEventListener("pointerup", handlePointerUp); +// canvasElement.addEventListener("pointermove", handlePointerMove); +// canvasElement.addEventListener("pointerup", handlePointerUp); - return () => { - canvasElement.removeEventListener("pointermove", handlePointerMove); - canvasElement.removeEventListener("pointerup", handlePointerUp); - }; - }, [selectedItemsIndex, selectedVersion?.versionId]); +// return () => { +// canvasElement.removeEventListener("pointermove", handlePointerMove); +// canvasElement.removeEventListener("pointerup", handlePointerUp); +// }; +// }, [selectedItemsIndex, selectedVersion?.versionId]); - useEffect(() => { - const canvasElement = state.gl.domElement; - let drag = false; - let isLeftMouseDown = false; +// 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 onMouseDown = (evt: any) => { +// if (evt.button === 0) { +// isLeftMouseDown = true; +// drag = false; +// } +// }; - const onMouseUp = (evt: any) => { - if (evt.button === 0) { - isLeftMouseDown = false; - if (!drag && toolMode === '3D-Delete' && activeModule === "builder") { - DeleteWallItems( - hoveredDeletableWallItem, - setWallItems, - wallItems, - socket, - projectId, - selectedVersion?.versionId || '', - ); - } - } - }; +// const onMouseUp = (evt: any) => { +// if (evt.button === 0) { +// isLeftMouseDown = false; +// if (!drag && toolMode === '3D-Delete' && activeModule === "builder") { +// DeleteWallItems( +// hoveredDeletableWallItem, +// setWallItems, +// wallItems, +// socket, +// projectId, +// selectedVersion?.versionId || '', +// ); +// } +// } +// }; - const onMouseMove = () => { - if (isLeftMouseDown) { - drag = true; - } - }; +// const onMouseMove = () => { +// if (isLeftMouseDown) { +// drag = true; +// } +// }; - const onDrop = (event: any) => { - if (selectedItem.category !== 'Fenestration') return; +// const onDrop = (event: any) => { +// if (selectedItem.category !== 'Fenestration') return; - pointer.x = (event.clientX / window.innerWidth) * 2 - 1; - pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; +// pointer.x = (event.clientX / window.innerWidth) * 2 - 1; +// pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; - raycaster.setFromCamera(pointer, camera); +// raycaster.setFromCamera(pointer, camera); - if (selectedItem.id && selectedVersion && projectId) { - if (selectedItem.subCategory) { - AddWallItems( - selectedItem, - raycaster, - CSGGroup, - setWallItems, - socket, - projectId, - selectedVersion?.versionId || '', - ); - } - event.preventDefault(); - } - }; +// if (selectedItem.id && selectedVersion && projectId) { +// if (selectedItem.subCategory) { +// AddWallItems( +// selectedItem, +// raycaster, +// CSGGroup, +// setWallItems, +// socket, +// projectId, +// selectedVersion?.versionId || '', +// ); +// } +// event.preventDefault(); +// } +// }; - const onDragOver = (event: any) => { - event.preventDefault(); - }; +// const onDragOver = (event: any) => { +// event.preventDefault(); +// }; - canvasElement.addEventListener("mousedown", onMouseDown); - canvasElement.addEventListener("mouseup", onMouseUp); - canvasElement.addEventListener("mousemove", onMouseMove); - canvasElement.addEventListener("drop", onDrop); - canvasElement.addEventListener("dragover", onDragOver); +// canvasElement.addEventListener("mousedown", onMouseDown); +// canvasElement.addEventListener("mouseup", onMouseUp); +// canvasElement.addEventListener("mousemove", onMouseMove); +// canvasElement.addEventListener("drop", onDrop); +// canvasElement.addEventListener("dragover", onDragOver); - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - canvasElement.removeEventListener("drop", onDrop); - canvasElement.removeEventListener("dragover", onDragOver); - }; - }, [toolMode, wallItems, selectedItem, camera, selectedVersion?.versionId]); +// return () => { +// canvasElement.removeEventListener("mousedown", onMouseDown); +// canvasElement.removeEventListener("mouseup", onMouseUp); +// canvasElement.removeEventListener("mousemove", onMouseMove); +// canvasElement.removeEventListener("drop", onDrop); +// canvasElement.removeEventListener("dragover", onDragOver); +// }; +// }, [toolMode, wallItems, selectedItem, camera, selectedVersion?.versionId]); - useEffect(() => { - if (toolMode && activeModule === "builder") { - handleMeshMissed( - currentWallItem, - setSelectedWallItem, - setSelectedItemsIndex - ); - setSelectedWallItem(null); - setSelectedItemsIndex(null); - } - }, [toolMode]); +// useEffect(() => { +// if (toolMode && activeModule === "builder") { +// handleMeshMissed( +// currentWallItem, +// setSelectedWallItem, +// setSelectedItemsIndex +// ); +// setSelectedWallItem(null); +// setSelectedItemsIndex(null); +// } +// }, [toolMode]); - return ( - <> - {wallItems.map((item: Types.WallItem, index: number) => ( - - - - ))} - - ); -}; +// return ( +// <> +// {wallItems.map((item: Types.WallItem, index: number) => ( +// +// +// +// ))} +// +// ); +// }; -export default WallItemsGroup; +// export default WallItemsGroup; diff --git a/app/src/modules/builder/groups/wallsAndWallItems.tsx b/app/src/modules/builder/groups/wallsAndWallItems.tsx index d4d52ec..6961cf6 100644 --- a/app/src/modules/builder/groups/wallsAndWallItems.tsx +++ b/app/src/modules/builder/groups/wallsAndWallItems.tsx @@ -1,74 +1,74 @@ -import { Geometry } from "@react-three/csg"; -import { - useSelectedWallItem, - useToggleView, - useToolMode, - useWallItems, - useWalls, -} from "../../../store/builder/store"; -import handleMeshDown from "../eventFunctions/handleMeshDown"; -import handleMeshMissed from "../eventFunctions/handleMeshMissed"; -import WallsMesh from "./wallsMesh"; -import WallItemsGroup from "./wallItemsGroup"; +// import { Geometry } from "@react-three/csg"; +// import { +// useSelectedWallItem, +// useToggleView, +// useToolMode, +// useWallItems, +// useWalls, +// } from "../../../store/builder/store"; +// import handleMeshDown from "../eventFunctions/handleMeshDown"; +// import handleMeshMissed from "../eventFunctions/handleMeshMissed"; +// import WallsMesh from "./wallsMesh"; +// import WallItemsGroup from "./wallItemsGroup"; -const WallsAndWallItems = ({ - CSGGroup, - setSelectedItemsIndex, - selectedItemsIndex, - currentWallItem, - csg, - lines, - hoveredDeletableWallItem, -}: any) => { - const { walls } = useWalls(); - const { wallItems } = useWallItems(); - const { toggleView } = useToggleView(); - const { toolMode } = useToolMode(); - const { setSelectedWallItem } = useSelectedWallItem(); +// const WallsAndWallItems = ({ +// CSGGroup, +// setSelectedItemsIndex, +// selectedItemsIndex, +// currentWallItem, +// csg, +// lines, +// hoveredDeletableWallItem, +// }: any) => { +// const { walls } = useWalls(); +// const { wallItems } = useWallItems(); +// const { toggleView } = useToggleView(); +// const { toolMode } = useToolMode(); +// const { setSelectedWallItem } = useSelectedWallItem(); - return ( - { - if (toolMode === "cursor") { - handleMeshDown( - event, - currentWallItem, - setSelectedWallItem, - setSelectedItemsIndex, - wallItems, - toggleView - ); - } - }} - onPointerMissed={() => { - if (toolMode === "cursor") { - handleMeshMissed( - currentWallItem, - setSelectedWallItem, - setSelectedItemsIndex - ); - setSelectedWallItem(null); - setSelectedItemsIndex(null); - } - }} - > - - - - - - ); -}; +// return ( +// { +// if (toolMode === "cursor") { +// handleMeshDown( +// event, +// currentWallItem, +// setSelectedWallItem, +// setSelectedItemsIndex, +// wallItems, +// toggleView +// ); +// } +// }} +// onPointerMissed={() => { +// if (toolMode === "cursor") { +// handleMeshMissed( +// currentWallItem, +// setSelectedWallItem, +// setSelectedItemsIndex +// ); +// setSelectedWallItem(null); +// setSelectedItemsIndex(null); +// } +// }} +// > +// +// +// +// +// +// ); +// }; -export default WallsAndWallItems; +// export default WallsAndWallItems; diff --git a/app/src/modules/builder/groups/wallsMesh.tsx b/app/src/modules/builder/groups/wallsMesh.tsx index 6b22a8d..80468e5 100644 --- a/app/src/modules/builder/groups/wallsMesh.tsx +++ b/app/src/modules/builder/groups/wallsMesh.tsx @@ -1,82 +1,82 @@ -import * as THREE from "three"; -import * as Types from "../../../types/world/worldTypes"; -import * as CONSTANTS from "../../../types/world/worldConstants"; -import { Base } from "@react-three/csg"; -import { MeshDiscardMaterial } from "@react-three/drei"; -import { useUpdateScene, useWalls } from "../../../store/builder/store"; -import React, { useEffect } from "react"; -import { getLines } from "../../../services/factoryBuilder/lines/getLinesApi"; -import objectLinesToArray from "../geomentries/lines/lineConvertions/objectLinesToArray"; -import loadWalls from "../geomentries/walls/loadWalls"; -import texturePath from "../../../assets/textures/floor/wall-tex.png"; -import { useParams } from "react-router-dom"; -import { getUserData } from "../../../functions/getUserData"; -import { useVersionContext } from "../version/versionContext"; +// import * as THREE from "three"; +// import * as Types from "../../../types/world/worldTypes"; +// import * as CONSTANTS from "../../../types/world/worldConstants"; +// import { Base } from "@react-three/csg"; +// import { MeshDiscardMaterial } from "@react-three/drei"; +// import { useUpdateScene, useWalls } from "../../../store/builder/store"; +// import React, { useEffect } from "react"; +// import { getLines } from "../../../services/factoryBuilder/lines/getLinesApi"; +// import objectLinesToArray from "../geomentries/lines/lineConvertions/objectLinesToArray"; +// import loadWalls from "../geomentries/walls/loadWalls"; +// import texturePath from "../../../assets/textures/floor/wall-tex.png"; +// import { useParams } from "react-router-dom"; +// import { getUserData } from "../../../functions/getUserData"; +// import { useVersionContext } from "../version/versionContext"; -const WallsMeshComponent = ({ lines }: any) => { - const { walls, setWalls } = useWalls(); - const { updateScene, setUpdateScene } = useUpdateScene(); - const { projectId } = useParams(); - const { selectedVersionStore } = useVersionContext(); - const { selectedVersion } = selectedVersionStore(); - const { organization } = getUserData(); +// const WallsMeshComponent = ({ lines }: any) => { +// const { walls, setWalls } = useWalls(); +// const { updateScene, setUpdateScene } = useUpdateScene(); +// const { projectId } = useParams(); +// const { selectedVersionStore } = useVersionContext(); +// const { selectedVersion } = selectedVersionStore(); +// const { organization } = getUserData(); - useEffect(() => { - if (updateScene) { - if (!selectedVersion) { - setUpdateScene(false); - return; - }; - getLines(organization, projectId, selectedVersion?.versionId || '').then((data) => { - const Lines: Types.Lines = objectLinesToArray(data); - localStorage.setItem("Lines", JSON.stringify(Lines)); +// useEffect(() => { +// if (updateScene) { +// if (!selectedVersion) { +// setUpdateScene(false); +// return; +// }; +// getLines(organization, projectId, selectedVersion?.versionId || '').then((data) => { +// const Lines: Types.Lines = objectLinesToArray(data); +// localStorage.setItem("Lines", JSON.stringify(Lines)); - if (Lines) { - loadWalls(lines, setWalls); - } - }); - setUpdateScene(false); - } - }, [updateScene, selectedVersion?.versionId]); +// if (Lines) { +// loadWalls(lines, setWalls); +// } +// }); +// setUpdateScene(false); +// } +// }, [updateScene, selectedVersion?.versionId]); - const textureLoader = new THREE.TextureLoader(); - const wallTexture = textureLoader.load(texturePath); +// const textureLoader = new THREE.TextureLoader(); +// const wallTexture = textureLoader.load(texturePath); - wallTexture.wrapS = wallTexture.wrapT = THREE.RepeatWrapping; - wallTexture.repeat.set(0.1, 0.1); - wallTexture.colorSpace = THREE.SRGBColorSpace; +// wallTexture.wrapS = wallTexture.wrapT = THREE.RepeatWrapping; +// wallTexture.repeat.set(0.1, 0.1); +// wallTexture.colorSpace = THREE.SRGBColorSpace; - return ( - <> - {walls.map((wall: Types.Wall, index: number) => ( - - - - - - - - - ))} - - ); -}; +// return ( +// <> +// {walls.map((wall: Types.Wall, index: number) => ( +// +// +// +// +// +// +// +// +// ))} +// +// ); +// }; -const WallsMesh = React.memo(WallsMeshComponent); -export default WallsMesh; +// const WallsMesh = React.memo(WallsMeshComponent); +// export default WallsMesh; diff --git a/app/src/modules/builder/groups/zoneGroup.tsx b/app/src/modules/builder/groups/zoneGroup.tsx deleted file mode 100644 index 24f6ee4..0000000 --- a/app/src/modules/builder/groups/zoneGroup.tsx +++ /dev/null @@ -1,651 +0,0 @@ -import React, { useState, useEffect, useMemo, useRef } from "react"; -import { Html, Line, Sphere } from "@react-three/drei"; -import { useThree, useFrame } from "@react-three/fiber"; -import * as THREE from "three"; -import { - useActiveLayer, - useSocketStore, - useToggleView, - useToolMode, - useRemovedLayer, - useZones, - useZonePoints, -} from "../../../store/builder/store"; -import { getZonesApi } from "../../../services/factoryBuilder/zones/getZonesApi"; - -import * as CONSTANTS from "../../../types/world/worldConstants"; -import * as turf from "@turf/turf"; -import { computeArea } from "../functions/computeArea"; -import { useSelectedZoneStore } from "../../../store/visualization/useZoneStore"; -import { useParams } from "react-router-dom"; -import { getUserData } from "../../../functions/getUserData"; -import { useVersionContext } from "../version/versionContext"; - -const ZoneGroup: React.FC = () => { - const { camera, pointer, gl, raycaster, scene, controls } = useThree(); - const [startPoint, setStartPoint] = useState(null); - const [endPoint, setEndPoint] = useState(null); - const { zones, setZones } = useZones(); - const { zonePoints, setZonePoints } = useZonePoints(); - const [isDragging, setIsDragging] = useState(false); - const { selectedZone } = useSelectedZoneStore(); - const [draggedSphere, setDraggedSphere] = useState(null); - const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); - const { toggleView } = useToggleView(); - const { removedLayer, setRemovedLayer } = useRemovedLayer(); - const { toolMode } = useToolMode(); - const { activeLayer } = useActiveLayer(); - const { socket } = useSocketStore(); - const { selectedVersionStore } = useVersionContext(); - const { selectedVersion } = selectedVersionStore(); - const { projectId } = useParams(); - const { userId, organization } = getUserData(); - - const groupsRef = useRef(); - - const zoneMaterial = useMemo( - () => - new THREE.ShaderMaterial({ - side: THREE.DoubleSide, - vertexShader: ` - varying vec2 vUv; - void main(){ - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); - vUv = uv; - } - `, - fragmentShader: ` - varying vec2 vUv; - uniform vec3 uOuterColor; - void main(){ - float alpha = 1.0 - vUv.y; - gl_FragColor = vec4(uOuterColor, alpha); - } - `, - uniforms: { - uOuterColor: { value: new THREE.Color(CONSTANTS.zoneConfig.color) }, - }, - transparent: true, - depthWrite: false, - }), - [] - ); - - useEffect(() => { - if (!selectedVersion) return; - getZonesApi(organization, projectId, selectedVersion?.versionId || '').then((data) => { - if (data && data.length > 0) { - const fetchedZones = data.map((zone: any) => ({ - zoneUuid: zone.zoneUuid, - zoneName: zone.zoneName, - points: zone.points, - viewPortCenter: zone.viewPortCenter, - viewPortposition: zone.viewPortposition, - layer: zone.layer, - })); - - setZones(fetchedZones); - - const fetchedPoints = data.flatMap((zone: any) => - zone.points.slice(0, 4).map((point: [number, number, number]) => new THREE.Vector3(...point)) - ); - - setZonePoints(fetchedPoints); - } else { - setZones([]); - } - }).catch((err) => { - console.error(err); - }) - }, [selectedVersion?.versionId]); - - useEffect(() => { - localStorage.setItem("zones", JSON.stringify(zones)); - }, [zones]); - - useEffect(() => { - if (removedLayer) { - const updatedZones = zones.filter((zone: any) => zone.layer !== removedLayer); - setZones(updatedZones); - - const updatedzonePoints = zonePoints.filter((_: any, index: any) => { - const zoneIndex = Math.floor(index / 4); - return zones[zoneIndex]?.layer !== removedLayer; - }); - setZonePoints(updatedzonePoints); - - zones.filter((zone: any) => zone.layer === removedLayer).forEach((zone: any) => { deleteZoneFromBackend(zone.zoneUuid); }); - - setRemovedLayer(null); - } - }, [removedLayer]); - - useEffect(() => { - if (toolMode !== "Zone") { - setStartPoint(null); - setEndPoint(null); - } - if (!toggleView) { - setStartPoint(null); - setEndPoint(null); - } - }, [toolMode, toggleView]); - - // eslint-disable-next-line react-hooks/exhaustive-deps - const addZoneToBackend = async (zone: { - zoneUuid: string; - zoneName: string; - points: [number, number, number][]; - layer: string; - }) => { - - const calculateCenter = (points: number[][]) => { - if (!points || points.length === 0) return null; - - let sumX = 0, sumY = 0, sumZ = 0; - const numPoints = points.length; - - points.forEach(([x, y, z]) => { - sumX += x; - sumY += y; - sumZ += z; - }); - - return [sumX / numPoints, sumY / numPoints, sumZ / numPoints] as [ - number, - number, - number - ]; - }; - - const target: [number, number, number] | null = calculateCenter(zone.points); - if (!target || zone.points.length < 4) return; - const position = [target[0], 10, target[2]]; - - const input = { - userId: userId, - versionId: selectedVersion?.versionId || '', - projectId, - organization, - zoneData: { - zoneName: zone.zoneName, - zoneUuid: zone.zoneUuid, - points: zone.points, - viewPortCenter: target, - viewPortposition: position, - layer: zone.layer, - }, - }; - - socket.emit("v1:zone:set", input); - }; - - // eslint-disable-next-line react-hooks/exhaustive-deps - const updateZoneToBackend = async (zone: { - zoneUuid: string; - zoneName: string; - points: [number, number, number][]; - layer: string; - }) => { - - const calculateCenter = (points: number[][]) => { - if (!points || points.length === 0) return null; - - let sumX = 0, sumY = 0, sumZ = 0; - const numPoints = points.length; - - points.forEach(([x, y, z]) => { - sumX += x; - sumY += y; - sumZ += z; - }); - - return [sumX / numPoints, sumY / numPoints, sumZ / numPoints] as [ - number, - number, - number - ]; - }; - - const target: [number, number, number] | null = calculateCenter(zone.points); - if (!target || zone.points.length < 4) return; - const position = [target[0], 10, target[2]]; - - const input = { - userId: userId, - versionId: selectedVersion?.versionId || '', - projectId, - organization, - zoneData: { - zoneName: zone.zoneName, - zoneUuid: zone.zoneUuid, - points: zone.points, - viewPortCenter: target, - viewPortposition: position, - layer: zone.layer, - }, - }; - - socket.emit("v1:zone:set", input); - }; - - const deleteZoneFromBackend = async (zoneUuid: string) => { - - const input = { - userId: userId, - versionId: selectedVersion?.versionId || '', - projectId, - organization, - zoneUuid: zoneUuid, - }; - - socket.emit("v1:zone:delete", input); - }; - - // eslint-disable-next-line react-hooks/exhaustive-deps - const handleDeleteZone = (zoneUuid: string) => { - const updatedZones = zones.filter((zone: any) => zone.zoneUuid !== zoneUuid); - setZones(updatedZones); - - const zoneIndex = zones.findIndex((zone: any) => zone.zoneUuid === zoneUuid); - if (zoneIndex !== -1) { - const zonePointsToRemove = zonePoints.slice(zoneIndex * 4, zoneIndex * 4 + 4); - zonePointsToRemove.forEach((point: any) => groupsRef.current.remove(point)); - const updatedzonePoints = zonePoints.filter((_: any, index: any) => index < zoneIndex * 4 || index >= zoneIndex * 4 + 4); - setZonePoints(updatedzonePoints); - } - deleteZoneFromBackend(zoneUuid); - }; - - useEffect(() => { - if (!camera || !toggleView) return; - const canvasElement = gl.domElement; - - let drag = false; - let isLeftMouseDown = false; - - const onMouseDown = (evt: any) => { - if (evt.button === 0) { - isLeftMouseDown = true; - drag = false; - - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster.intersectObjects(groupsRef.current.children, true); - - if (intersects.length > 0 && toolMode === "move") { - const clickedObject = intersects[0].object; - const sphereIndex = zonePoints.findIndex((point: any) => - point.equals(clickedObject.position) - ); - if (sphereIndex !== -1) { - (controls as any).enabled = false; - setDraggedSphere(zonePoints[sphereIndex]); - setIsDragging(true); - } - } - } - }; - - const onMouseUp = (evt: any) => { - if (evt.button === 0 && !drag && !isDragging && toolMode === 'Zone') { - isLeftMouseDown = false; - - if (!startPoint) { - raycaster.setFromCamera(pointer, camera); - const intersectionPoint = new THREE.Vector3(); - const point = raycaster.ray.intersectPlane(plane, intersectionPoint); - if (point) { - setStartPoint(point); - setEndPoint(null); - } - } else if (startPoint) { - raycaster.setFromCamera(pointer, camera); - const intersectionPoint = new THREE.Vector3(); - const point = raycaster.ray.intersectPlane(plane, intersectionPoint); - if (!point) return; - - const points = [ - [startPoint.x, 0.15, startPoint.z], - [point.x, 0.15, startPoint.z], - [point.x, 0.15, point.z], - [startPoint.x, 0.15, point.z], - [startPoint.x, 0.15, startPoint.z], - ] as [number, number, number][]; - - const zoneName = `Zone ${zones.length + 1}`; - const zoneUuid = THREE.MathUtils.generateUUID(); - const newZone = { - zoneUuid, - zoneName, - points: points, - layer: activeLayer, - }; - - const newZones = [...zones, newZone]; - - setZones(newZones); - - const newzonePoints = [ - new THREE.Vector3(startPoint.x, 0.15, startPoint.z), - new THREE.Vector3(point.x, 0.15, startPoint.z), - new THREE.Vector3(point.x, 0.15, point.z), - new THREE.Vector3(startPoint.x, 0.15, point.z), - ]; - - const updatedZonePoints = [...zonePoints, ...newzonePoints]; - setZonePoints(updatedZonePoints); - - addZoneToBackend(newZone); - setStartPoint(null); - setEndPoint(null); - } - } else if (evt.button === 0 && !drag && !isDragging && toolMode === '2D-Delete') { - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster.intersectObjects( - groupsRef.current.children, - true - ); - - if (intersects.length > 0) { - const clickedObject = intersects[0].object; - - const sphereIndex = zonePoints.findIndex((point: any) => - point.equals(clickedObject.position) - ); - if (sphereIndex !== -1) { - const zoneIndex = Math.floor(sphereIndex / 4); - const zoneUuid = zones[zoneIndex].zoneUuid; - handleDeleteZone(zoneUuid); - return; - } - } - } - - if (evt.button === 0) { - if (isDragging && draggedSphere) { - setIsDragging(false); - setDraggedSphere(null); - - const sphereIndex = zonePoints.findIndex((point: any) => point === draggedSphere); - if (sphereIndex !== -1) { - const zoneIndex = Math.floor(sphereIndex / 4); - - if (zoneIndex !== -1 && zones[zoneIndex]) { - updateZoneToBackend(zones[zoneIndex]); - } - } - } - } - }; - - const onMouseMove = () => { - if (!groupsRef.current) return; - if (isLeftMouseDown) { - drag = true; - } - raycaster.setFromCamera(pointer, camera); - - if (isDragging && draggedSphere) { - raycaster.setFromCamera(pointer, camera); - const intersectionPoint = new THREE.Vector3(); - const point = raycaster.ray.intersectPlane(plane, intersectionPoint); - if (point) { - draggedSphere.set(point.x, 0.15, point.z); - - const sphereIndex = zonePoints.findIndex((point: any) => point === draggedSphere); - if (sphereIndex !== -1) { - const zoneIndex = Math.floor(sphereIndex / 4); - const cornerIndex = sphereIndex % 4; - - const updatedZones = zones.map((zone: any, index: number) => { - if (index === zoneIndex) { - const updatedPoints = [...zone.points]; - updatedPoints[cornerIndex] = [point.x, 0.15, point.z]; - updatedPoints[4] = updatedPoints[0]; - return { ...zone, points: updatedPoints }; - } - return zone; - }); - - setZones(updatedZones); - } - } - } - }; - - const onContext = (event: any) => { - event.preventDefault(); - setStartPoint(null); - setEndPoint(null); - }; - - if (toolMode === "Zone" || toolMode === '2D-Delete' || toolMode === "move") { - canvasElement.addEventListener("mousedown", onMouseDown); - canvasElement.addEventListener("mouseup", onMouseUp); - canvasElement.addEventListener("mousemove", onMouseMove); - canvasElement.addEventListener("contextmenu", onContext); - } - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - canvasElement.removeEventListener("contextmenu", onContext); - }; - }, [gl, camera, startPoint, toggleView, scene, toolMode, zones, isDragging, zonePoints, draggedSphere, activeLayer, raycaster, pointer, controls, plane, setZones, setZonePoints, addZoneToBackend, handleDeleteZone, updateZoneToBackend, selectedVersion?.versionId]); - - useFrame(() => { - if (!startPoint) return; - raycaster.setFromCamera(pointer, camera); - const intersectionPoint = new THREE.Vector3(); - const point = raycaster.ray.intersectPlane(plane, intersectionPoint); - if (point) { - setEndPoint(point); - } - }); - - return ( - - - {zones.map((zone: any) => ( - - {zone.points - .slice(0, -1) - .map((point: [number, number, number], index: number) => { - const nextPoint = zone.points[index + 1]; - - const point1 = new THREE.Vector3(point[0], point[1], point[2]); - const point2 = new THREE.Vector3( - nextPoint[0], - nextPoint[1], - nextPoint[2] - ); - - const planeWidth = point1.distanceTo(point2); - const planeHeight = CONSTANTS.zoneConfig.height; - - const midpoint = new THREE.Vector3( - (point1.x + point2.x) / 2, - CONSTANTS.zoneConfig.height / 2 + - (zone.layer - 1) * CONSTANTS.zoneConfig.height, - (point1.z + point2.z) / 2 - ); - - const angle = Math.atan2( - point2.z - point1.z, - point2.x - point1.x - ); - - return ( - - - - - ); - })} - {!toggleView && - (() => { - const points3D = zone.points || []; - const coords2D = points3D.map((p: any) => [p[0], p[2]]); - - // Ensure the polygon is closed - if ( - 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; - - // Calculate the average Y value - const sumY = points3D.reduce( - (sum: number, p: any) => sum + p[1], - 0 - ); - const avgY = points3D.length > 0 ? sumY / points3D.length : 0; - - const htmlPosition: [number, number, number] = [ - center2D[0], - avgY + (CONSTANTS.zoneConfig.height || 0) + 1.5, - center2D[1], - ]; - - return ( - -
{zone.zoneName}
- - ); - })()} -
- ))} -
- - {zones - .filter((zone: any) => zone.layer === activeLayer) - .map((zone: any) => ( - { - e.stopPropagation(); - if (toolMode === '2D-Delete') { - handleDeleteZone(zone.zoneUuid); - } - }} - /> - ))} - - - {zones.map((zone: any, index: any) => { - if (!toggleView) return null; - const points3D = zone.points; - const coords2D = points3D.map((p: any) => [p[0], p[2]]); - - if ( - 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; - - const sumY = points3D.reduce((sum: number, p: any) => sum + p[1], 0); - const avgY = sumY / points3D.length; - - const area = computeArea(points3D, "zone"); - const formattedArea = `${area.toFixed(2)} m²`; - - const htmlPosition: [number, number, number] = [ - center2D[0], - avgY + CONSTANTS.zoneConfig.height, - center2D[1], - ]; - return ( - -
- {zone.zoneName} ({formattedArea}) -
- - ); - })} -
- - - {zones - .filter((zone: any) => zone.layer === activeLayer) - .flatMap((zone: any) => - zone.points.slice(0, 4).map((point: any, pointIndex: number) => ( - - - - )) - )} - - - {startPoint && endPoint && ( - - )} - -
- ); -}; - -export default ZoneGroup; diff --git a/app/src/modules/builder/geomentries/lines/getClosestIntersection.ts b/app/src/modules/builder/line/helpers/getClosestIntersection.ts similarity index 96% rename from app/src/modules/builder/geomentries/lines/getClosestIntersection.ts rename to app/src/modules/builder/line/helpers/getClosestIntersection.ts index afae9d5..e5a803d 100644 --- a/app/src/modules/builder/geomentries/lines/getClosestIntersection.ts +++ b/app/src/modules/builder/line/helpers/getClosestIntersection.ts @@ -1,26 +1,26 @@ -import * as THREE from 'three'; - -import * as Types from "../../../../types/world/worldTypes"; - -function getClosestIntersection( - intersects: Types.Vector3Array, - point: Types.Vector3 -): Types.Vector3 { - - ////////// A function that finds which point is closest from the intersects points that is given, Used in finding which point in a line is closest when clicked on a line during drawing ////////// - - let closestNewPoint: THREE.Vector3 = point; - let minDistance = Infinity; - - for (const intersect of intersects) { - const distance = point.distanceTo(intersect); - if (distance < minDistance) { - minDistance = distance; - closestNewPoint = intersect; - } - } - - return closestNewPoint; -} - -export default getClosestIntersection; +import * as THREE from 'three'; + +import * as Types from "../../../../types/world/worldTypes"; + +function getClosestIntersection( + intersects: Types.Vector3Array, + point: Types.Vector3 +): Types.Vector3 { + + ////////// A function that finds which point is closest from the intersects points that is given, Used in finding which point in a line is closest when clicked on a line during drawing ////////// + + let closestNewPoint: THREE.Vector3 = point; + let minDistance = Infinity; + + for (const intersect of intersects) { + const distance = point.distanceTo(intersect); + if (distance < minDistance) { + minDistance = distance; + closestNewPoint = intersect; + } + } + + return closestNewPoint; +} + +export default getClosestIntersection; diff --git a/app/src/modules/builder/line/helpers/getClosestPointOnLineSegment.ts b/app/src/modules/builder/line/helpers/getClosestPointOnLineSegment.ts new file mode 100644 index 0000000..8b65282 --- /dev/null +++ b/app/src/modules/builder/line/helpers/getClosestPointOnLineSegment.ts @@ -0,0 +1,12 @@ +import { Vector3 } from "three"; + +export default function closestPointOnLineSegment(p: Vector3, a: Vector3, b: Vector3) { + const ab = new Vector3().subVectors(b, a); + const ap = new Vector3().subVectors(p, a); + + const abLengthSq = ab.lengthSq(); + const dot = ap.dot(ab); + const t = Math.max(0, Math.min(1, dot / abLengthSq)); + + return new Vector3().copy(a).add(ab.multiplyScalar(t)); +} \ No newline at end of file diff --git a/app/src/modules/builder/line/line.tsx b/app/src/modules/builder/line/line.tsx index 19f0810..cc53b9d 100644 --- a/app/src/modules/builder/line/line.tsx +++ b/app/src/modules/builder/line/line.tsx @@ -2,10 +2,20 @@ import * as THREE from 'three'; import { useThree } from '@react-three/fiber'; import { useEffect, useMemo, useState } from "react"; import { DragControls, Tube } from '@react-three/drei'; -import { useToolMode } from '../../../store/builder/store'; +import { useSocketStore, useToolMode } from '../../../store/builder/store'; import { useBuilderStore } from '../../../store/builder/useBuilderStore'; import { useSceneContext } from '../../scene/sceneContext'; import * as Constants from '../../../types/world/worldConstants'; +import { useVersionContext } from '../version/versionContext'; +import { useParams } from 'react-router-dom'; +import { getUserData } from '../../../functions/getUserData'; + +// import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi'; +// import { deleteWallApi } from '../../../services/factoryBuilder/wall/deleteWallApi'; +// import { upsertFloorApi } from '../../../services/factoryBuilder/floor/upsertFloorApi'; +// import { deleteFloorApi } from '../../../services/factoryBuilder/floor/deleteFloorApi'; +// import { deleteZoneApi } from '../../../services/factoryBuilder/zone/deleteZoneApi'; +// import { upsertZoneApi } from '../../../services/factoryBuilder/zone/upsertZoneApi'; interface LineProps { points: [Point, Point]; @@ -16,9 +26,16 @@ function Line({ points }: Readonly) { const { raycaster, camera, pointer, gl } = useThree(); const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); const [isDeletable, setIsDeletable] = useState(false); + const { socket } = useSocketStore(); const { toolMode } = useToolMode(); - const { wallStore } = useSceneContext(); - const { removeWallByPoints, setPosition } = wallStore(); + const { wallStore, floorStore, zoneStore } = useSceneContext(); + const { removeWallByPoints, setPosition: setWallPosition, getWallsByPointId } = wallStore(); + const { removeFloorByPoints, setPosition: setFloorPosition, getFloorsByPointId } = floorStore(); + const { removeZoneByPoints, setPosition: setZonePosition, getZonesByPointId } = zoneStore(); + const { userId, organization } = getUserData(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { projectId } = useParams(); const [dragOffset, setDragOffset] = useState(null); const { hoveredLine, setHoveredLine, hoveredPoint } = useBuilderStore(); @@ -79,9 +96,123 @@ function Line({ points }: Readonly) { const handlePointClick = (points: [Point, Point]) => { if (toolMode === '2D-Delete') { if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') { - removeWallByPoints(points); + const removedWall = removeWallByPoints(points); + if (removedWall && projectId) { + + // API + + // deleteWallApi(projectId, selectedVersion?.versionId || '', removedWall.wallUuid); + + // SOCKET + + const data = { + wallUuid: removedWall.wallUuid, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:delete', data); + } setHoveredLine(null); } + if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') { + const { removedFloors, updatedFloors } = removeFloorByPoints(points); + if (removedFloors.length > 0) { + removedFloors.forEach(floor => { + if (projectId) { + + // API + + // deleteFloorApi(projectId, selectedVersion?.versionId || '', floor.floorUuid); + + // SOCKET + + const data = { + floorUuid: floor.floorUuid, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:delete', data); + } + }); + } + if (updatedFloors.length > 0) { + updatedFloors.forEach(floor => { + if (projectId) { + + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', floor); + + // SOCKET + + const data = { + floorData: floor, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + } + }); + } + + setHoveredLine(null); + } + if (points[0].pointType === 'Zone' && points[1].pointType === 'Zone') { + const { removedZones, updatedZones } = removeZoneByPoints(points); + if (removedZones.length > 0) { + removedZones.forEach(zone => { + if (projectId) { + + // API + + // deleteZoneApi(projectId, selectedVersion?.versionId || '', zone.zoneUuid); + + // SOCKET + + const data = { + zoneUuid: zone.zoneUuid, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:delete', data); + } + }); + } + if (updatedZones.length > 0) { + updatedZones.forEach(zone => { + if (projectId) { + + // API + + // upsertZoneApi(projectId, selectedVersion?.versionId || '', zone); + + // SOCKET + + const data = { + zoneData: zone, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:add', data); + } + }); + } + } gl.domElement.style.cursor = 'default'; } } @@ -105,8 +236,18 @@ function Line({ points }: Readonly) { const newStart = new THREE.Vector3().addVectors(start, delta); const newEnd = new THREE.Vector3().addVectors(end, delta); - setPosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]); - setPosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]); + if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') { + setWallPosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]); + setWallPosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]); + } + if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') { + setFloorPosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]); + setFloorPosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]); + } + if (points[0].pointType === 'Zone' && points[1].pointType === 'Zone') { + setZonePosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]); + setZonePosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]); + } } } }; @@ -127,11 +268,90 @@ function Line({ points }: Readonly) { }; const handleDragEnd = (points: [Point, Point]) => { + if (toolMode !== 'move' || !dragOffset) return; gl.domElement.style.cursor = 'default'; setDragOffset(null); - if (toolMode !== 'move') return; if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') { - // console.log('Wall after drag: ', points); + const updatedWalls1 = getWallsByPointId(points[0].pointUuid); + const updatedWalls2 = getWallsByPointId(points[1].pointUuid); + const updatedWalls = [...updatedWalls1, ...updatedWalls2].filter((wall, index, self) => index === self.findIndex((w) => w.wallUuid === wall.wallUuid)); + + if (updatedWalls.length > 0 && projectId) { + updatedWalls.forEach(updatedWall => { + + // API + + // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall).catch((error) => { + // console.error('Error updating wall:', error); + // }); + + // SOCKET + + const data = { + wallData: updatedWall, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + }) + } + } else if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') { + const updatedFloors1 = getFloorsByPointId(points[0].pointUuid); + const updatedFloors2 = getFloorsByPointId(points[1].pointUuid); + const updatedFloors = [...updatedFloors1, ...updatedFloors2].filter((floor, index, self) => index === self.findIndex((f) => f.floorUuid === floor.floorUuid)); + + if (updatedFloors.length > 0 && projectId) { + updatedFloors.forEach(updatedFloor => { + + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor).catch((error) => { + // console.error('Error updating floor:', error); + // }); + + // SOCKET + + const data = { + floorData: updatedFloor, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + }) + } + } else if (points[0].pointType === 'Zone' && points[1].pointType === 'Zone') { + const updatedZones1 = getZonesByPointId(points[0].pointUuid); + const updatedZones2 = getZonesByPointId(points[1].pointUuid); + const updatedZones = [...updatedZones1, ...updatedZones2].filter((zone, index, self) => index === self.findIndex((z) => z.zoneUuid === zone.zoneUuid)); + + if (updatedZones.length > 0 && projectId) { + updatedZones.forEach(updatedZone => { + + // API + + // upsertZoneApi(projectId, selectedVersion?.versionId || '', updatedZone).catch((error) => { + // console.error('Error updating zone:', error); + // }); + + // SOCKET + + const data = { + zoneData: updatedZone, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:add', data); + }) + } } } diff --git a/app/src/modules/builder/pillars/addAndUpdateReferencePillar.ts b/app/src/modules/builder/pillars/addAndUpdateReferencePillar.ts new file mode 100644 index 0000000..31407ee --- /dev/null +++ b/app/src/modules/builder/pillars/addAndUpdateReferencePillar.ts @@ -0,0 +1,54 @@ +// import * as THREE from 'three'; +// import updateReferencePolesheight from './updateReferencePolesheight'; + +// import * as Types from "../../../../types/world/worldTypes"; + +// function addAndUpdateReferencePillar( +// raycaster: THREE.Raycaster, +// floorGroup: Types.RefGroup, +// referencePole: Types.RefMesh +// ): void { + +// ////////// Find Pillars position and scale based on the pointer interaction ////////// + +// let Roofs = raycaster.intersectObjects(floorGroup.current.children, true); +// const intersected = Roofs.find(intersect => intersect.object.name.includes("Roof") || intersect.object.name.includes("Floor")); + +// if (intersected) { +// const intersectionPoint = intersected.point; +// raycaster.ray.origin.copy(intersectionPoint); +// raycaster.ray.direction.set(0, -1, 0); +// const belowIntersections = raycaster.intersectObjects(floorGroup.current.children, true); +// const validIntersections = belowIntersections.filter(intersect => intersect.object.name.includes("Floor")); + +// let distance: Types.Number; + +// if (validIntersections.length > 1) { +// let valid = validIntersections.find(intersectedBelow => intersected.point.distanceTo(intersectedBelow.point) > 3); +// if (valid) { +// updateReferencePolesheight(intersectionPoint, valid.distance, referencePole, floorGroup); +// } else { +// const belowPoint = new THREE.Vector3(intersectionPoint.x, 0, intersectionPoint.z); +// distance = intersected.point.distanceTo(belowPoint); +// if (distance > 3) { +// updateReferencePolesheight(intersectionPoint, distance, referencePole, floorGroup); +// } +// } +// } else { +// const belowPoint = new THREE.Vector3(intersectionPoint.x, 0, intersectionPoint.z); +// distance = intersected.point.distanceTo(belowPoint); +// if (distance > 3) { +// updateReferencePolesheight(intersectionPoint, distance, referencePole, floorGroup); +// } +// } +// } else { +// if (referencePole.current) { +// (referencePole.current.material).dispose(); +// (referencePole.current.geometry).dispose(); +// floorGroup.current.remove(referencePole.current); +// referencePole.current = null; +// } +// } +// } + +// export default addAndUpdateReferencePillar; diff --git a/app/src/modules/builder/pillars/addPillar.ts b/app/src/modules/builder/pillars/addPillar.ts new file mode 100644 index 0000000..9839f16 --- /dev/null +++ b/app/src/modules/builder/pillars/addPillar.ts @@ -0,0 +1,24 @@ +// import * as THREE from 'three'; +// import * as CONSTANTS from '../../../../types/world/worldConstants'; +// import * as Types from "../../../../types/world/worldTypes"; + +// function addPillar( +// referencePole: Types.RefMesh, +// floorGroup: Types.RefGroup +// ): void { + +// ////////// Add Pillars to the scene based on the reference. current poles position and scale ////////// + +// if (referencePole.current) { +// let pole: THREE.Mesh; +// const geometry = referencePole.current.userData.geometry.clone(); +// const material = new THREE.MeshStandardMaterial({ color: CONSTANTS.columnConfig.defaultColor }); +// pole = new THREE.Mesh(geometry, material); +// pole.rotateX(Math.PI / 2); +// pole.name = "Pole"; +// pole.position.set(referencePole.current.userData.position.x, referencePole.current.userData.position.y, referencePole.current.userData.position.z); +// floorGroup.current.add(pole); +// } +// } + +// export default addPillar; \ No newline at end of file diff --git a/app/src/modules/builder/pillars/deletableHoveredPillar.ts b/app/src/modules/builder/pillars/deletableHoveredPillar.ts new file mode 100644 index 0000000..1751fdb --- /dev/null +++ b/app/src/modules/builder/pillars/deletableHoveredPillar.ts @@ -0,0 +1,34 @@ +// import * as THREE from 'three'; + +// import * as Types from "../../../../types/world/worldTypes"; + +// function DeletableHoveredPillar( +// state: Types.ThreeState, +// floorGroup: Types.RefGroup, +// hoveredDeletablePillar: Types.RefMesh +// ): void { + +// ////////// Altering the color of the hovered Pillar during the Deletion time ////////// + +// const intersects = state.raycaster.intersectObjects(floorGroup.current.children, true); +// const poleIntersect = intersects.find(intersect => intersect.object.name === "Pole"); + +// if (poleIntersect) { +// if (poleIntersect.object.name !== "Pole") { +// return; +// } +// if (hoveredDeletablePillar.current) { +// (hoveredDeletablePillar.current.material as THREE.MeshStandardMaterial).emissive = new THREE.Color("black"); +// hoveredDeletablePillar.current = undefined; +// } +// hoveredDeletablePillar.current = poleIntersect.object as THREE.Mesh; // Type assertion +// (hoveredDeletablePillar.current.material as THREE.MeshStandardMaterial).emissive = new THREE.Color("red"); +// } else { +// if (hoveredDeletablePillar.current) { +// (hoveredDeletablePillar.current.material as THREE.MeshStandardMaterial).emissive = new THREE.Color("black"); +// hoveredDeletablePillar.current = undefined; +// } +// } +// } + +// export default DeletableHoveredPillar; \ No newline at end of file diff --git a/app/src/modules/builder/pillars/deletePillar.ts b/app/src/modules/builder/pillars/deletePillar.ts new file mode 100644 index 0000000..22fc9bf --- /dev/null +++ b/app/src/modules/builder/pillars/deletePillar.ts @@ -0,0 +1,21 @@ +// import { toast } from 'react-toastify'; + +// import * as Types from "../../../../types/world/worldTypes"; + +// function DeletePillar( +// hoveredDeletablePillar: Types.RefMesh, +// floorGroup: Types.RefGroup +// ): void { + +// ////////// Deleting the hovered Pillar from the itemsGroup ////////// + +// if (hoveredDeletablePillar.current) { +// (hoveredDeletablePillar.current.material).dispose(); +// (hoveredDeletablePillar.current.geometry).dispose(); +// floorGroup.current.remove(hoveredDeletablePillar.current); +// echo.success("Pillar Removed!"); +// hoveredDeletablePillar.current = undefined; +// } +// } + +// export default DeletePillar; diff --git a/app/src/modules/builder/pillars/updateReferencePolesheight.ts b/app/src/modules/builder/pillars/updateReferencePolesheight.ts new file mode 100644 index 0000000..eaffd76 --- /dev/null +++ b/app/src/modules/builder/pillars/updateReferencePolesheight.ts @@ -0,0 +1,40 @@ +// import * as THREE from 'three'; + +// import * as Types from "../../../../types/world/worldTypes"; + +// function updateReferencePolesheight( +// intersectionPoint: Types.Vector3, +// distance: Types.Number, +// referencePole: Types.RefMesh, +// floorGroup: Types.RefGroup +// ): void { + +// ////////// Add a Reference Pillar and update its position and scale based on the pointer interaction ////////// + +// if (referencePole.current) { +// (referencePole.current.material).dispose(); +// (referencePole.current.geometry).dispose(); +// floorGroup.current.remove(referencePole.current); +// referencePole.current.geometry.dispose(); +// } + +// const shape = new THREE.Shape(); +// shape.moveTo(0.5, 0); +// shape.absarc(0, 0, 0.5, 0, 2 * Math.PI, false); + +// const extrudeSettings = { +// depth: distance, +// bevelEnabled: false, +// }; + +// const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); +// const material = new THREE.MeshBasicMaterial({ color: "green", transparent: true, opacity: 0.5 }); +// referencePole.current = new THREE.Mesh(geometry, material); +// referencePole.current.rotateX(Math.PI / 2); +// referencePole.current.position.set(intersectionPoint.x, intersectionPoint.y - 0.01, intersectionPoint.z); +// referencePole.current.userData = { geometry: geometry, distance: distance, position: { x: intersectionPoint.x, y: intersectionPoint.y - 0.01, z: intersectionPoint.z } }; + +// floorGroup.current.add(referencePole.current); +// } + +// export default updateReferencePolesheight; diff --git a/app/src/modules/builder/point/helpers/usePointSnapping.tsx b/app/src/modules/builder/point/helpers/usePointSnapping.tsx index c7d31cc..648e4c5 100644 --- a/app/src/modules/builder/point/helpers/usePointSnapping.tsx +++ b/app/src/modules/builder/point/helpers/usePointSnapping.tsx @@ -11,9 +11,11 @@ const ANGLE_SNAP_DISTANCE_THRESHOLD = 0.5; // Distance threshold for snapping i const CAN_ANGLE_SNAP = true; // Whether snapping is enabled or not export const usePointSnapping = (currentPoint: { uuid: string, pointType: string, position: [number, number, number] } | null) => { - const { aisleStore, wallStore } = useSceneContext(); + const { aisleStore, wallStore, floorStore, zoneStore } = useSceneContext(); const { aisles, getConnectedPoints: getConnectedAislePoints } = aisleStore(); const { walls, getConnectedPoints: getConnectedWallPoints } = wallStore(); + const { floors, getConnectedPoints: getConnectedFloorPoints } = floorStore(); + const { zones, getConnectedPoints: getConnectedZonePoints } = zoneStore(); // Wall Snapping @@ -24,10 +26,13 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string ); }, [walls, currentPoint]); - const snapWallPoint = useCallback((position: [number, number, number]) => { + const snapWallPoint = useCallback((position: [number, number, number], tempPoints?: Point) => { if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null }; const otherPoints = getAllOtherWallPoints(); + if (tempPoints) { + otherPoints.push(tempPoints); + } const currentVec = new THREE.Vector3(...position); for (const point of otherPoints) { const pointVec = new THREE.Vector3(...point.position); @@ -187,10 +192,188 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string }; }, [currentPoint, getConnectedAislePoints]); + // Floor Snapping + + const getAllOtherFloorPoints = useCallback(() => { + if (!currentPoint) return []; + + return floors.flatMap(floor => + floor.points.filter(point => point.pointUuid !== currentPoint.uuid) + ); + }, [floors, currentPoint]); + + const snapFloorPoint = useCallback((position: [number, number, number], tempPoints?: Point[] | []) => { + if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null }; + + let otherPoints = getAllOtherFloorPoints(); + if (tempPoints) { + otherPoints = [...otherPoints, ...tempPoints]; + } + 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 <= POINT_SNAP_THRESHOLD && currentPoint.pointType === 'Floor') { + return { position: point.position, isSnapped: true, snappedPoint: point }; + } + } + + return { position: position, isSnapped: false, snappedPoint: null }; + }, [currentPoint, getAllOtherFloorPoints]); + + const snapFloorAngle = useCallback((newPosition: [number, number, number]): { + position: [number, number, number], + isSnapped: boolean, + snapSources: THREE.Vector3[] + } => { + if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] }; + + const connectedPoints = getConnectedFloorPoints(currentPoint.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 < ANGLE_SNAP_DISTANCE_THRESHOLD) { + if (!closestX || xDist < closestX.dist) { + closestX = { pos: cPos, dist: xDist }; + } + } + + if (zDist < ANGLE_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 + }; + }, [currentPoint, getConnectedFloorPoints]); + + // Zone Snapping + + const getAllOtherZonePoints = useCallback(() => { + if (!currentPoint) return []; + + return zones.flatMap(zone => + zone.points.filter(point => point.pointUuid !== currentPoint.uuid) + ); + }, [zones, currentPoint]); + + const snapZonePoint = useCallback((position: [number, number, number], tempPoints?: Point[] | []) => { + if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null }; + + let otherPoints = getAllOtherZonePoints(); + if (tempPoints) { + otherPoints = [...otherPoints, ...tempPoints]; + } + 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 <= POINT_SNAP_THRESHOLD && currentPoint.pointType === 'Zone') { + return { position: point.position, isSnapped: true, snappedPoint: point }; + } + } + + return { position: position, isSnapped: false, snappedPoint: null }; + }, [currentPoint, getAllOtherZonePoints]); + + const snapZoneAngle = useCallback((newPosition: [number, number, number]): { + position: [number, number, number], + isSnapped: boolean, + snapSources: THREE.Vector3[] + } => { + if (!currentPoint || !CAN_ANGLE_SNAP) return { position: newPosition, isSnapped: false, snapSources: [] }; + + const connectedPoints = getConnectedZonePoints(currentPoint.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 < ANGLE_SNAP_DISTANCE_THRESHOLD) { + if (!closestX || xDist < closestX.dist) { + closestX = { pos: cPos, dist: xDist }; + } + } + + if (zDist < ANGLE_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 + }; + }, [currentPoint, getConnectedZonePoints]); + return { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle, + snapFloorPoint, + snapFloorAngle, + snapZonePoint, + snapZoneAngle, }; }; \ No newline at end of file diff --git a/app/src/modules/builder/point/point.tsx b/app/src/modules/builder/point/point.tsx index a671806..15777aa 100644 --- a/app/src/modules/builder/point/point.tsx +++ b/app/src/modules/builder/point/point.tsx @@ -1,29 +1,42 @@ import * as THREE from 'three'; import * as Constants from '../../../types/world/worldConstants'; import { useRef, useState, useEffect, useMemo } from 'react'; -import { useToolMode } from '../../../store/builder/store'; +import { useSocketStore, useToolMode } from '../../../store/builder/store'; import { DragControls } from '@react-three/drei'; import { useThree } from '@react-three/fiber'; import { useBuilderStore } from '../../../store/builder/useBuilderStore'; import { usePointSnapping } from './helpers/usePointSnapping'; -import { deleteAisleApi } from '../../../services/factoryBuilder/aisle/deleteAisleApi'; import { useParams } from 'react-router-dom'; -import { createAisleApi } from '../../../services/factoryBuilder/aisle/createAisleApi'; import { useVersionContext } from '../version/versionContext'; import { useSceneContext } from '../../scene/sceneContext'; +import { upsertAisleApi } from '../../../services/factoryBuilder/aisle/upsertAisleApi'; +import { deleteAisleApi } from '../../../services/factoryBuilder/aisle/deleteAisleApi'; +// import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi'; +// import { deleteWallApi } from '../../../services/factoryBuilder/wall/deleteWallApi'; +// import { upsertFloorApi } from '../../../services/factoryBuilder/floor/upsertFloorApi'; +// import { deleteFloorApi } from '../../../services/factoryBuilder/floor/deleteFloorApi'; +// import { upsertZoneApi } from '../../../services/factoryBuilder/zone/upsertZoneApi'; +// import { deleteZoneApi } from '../../../services/factoryBuilder/zone/deleteZoneApi'; + +import { getUserData } from '../../../functions/getUserData'; + function Point({ point }: { readonly point: Point }) { const materialRef = useRef(null); const { raycaster, camera, pointer, gl } = useThree(); const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); const [isHovered, setIsHovered] = useState(false); const [dragOffset, setDragOffset] = useState(null); + const { socket } = useSocketStore(); const { toolMode } = useToolMode(); - const { aisleStore, wallStore } = useSceneContext(); + const { aisleStore, wallStore, floorStore, zoneStore } = useSceneContext(); const { setPosition: setAislePosition, removePoint: removeAislePoint, getAislesByPointId } = aisleStore(); - const { setPosition: setWallPosition, removePoint: removeWallPoint } = wallStore(); - const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position }); + const { setPosition: setWallPosition, removePoint: removeWallPoint, getWallsByPointId } = wallStore(); + const { setPosition: setFloorPosition, removePoint: removeFloorPoint, getFloorsByPointId } = floorStore(); + const { setPosition: setZonePosition, removePoint: removeZonePoint, getZonesByPointId } = zoneStore(); + const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle, snapFloorPoint, snapFloorAngle, snapZonePoint, snapZoneAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position }); const { hoveredPoint, setHoveredPoint } = useBuilderStore(); + const { userId, organization } = getUserData(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); const { projectId } = useParams(); @@ -113,6 +126,14 @@ function Point({ point }: { readonly point: Point }) { const wallSnapped = snapWallAngle(newPosition); const finalSnapped = snapWallPoint(wallSnapped.position); setWallPosition(point.pointUuid, finalSnapped.position); + } else if (point.pointType === 'Floor') { + const floorSnapped = snapFloorAngle(newPosition); + const finalSnapped = snapFloorPoint(floorSnapped.position); + setFloorPosition(point.pointUuid, finalSnapped.position); + } else if (point.pointType === 'Zone') { + const zoneSnapped = snapZoneAngle(newPosition); + const finalSnapped = snapZonePoint(zoneSnapped.position); + setZonePosition(point.pointUuid, finalSnapped.position); } } } @@ -138,11 +159,75 @@ function Point({ point }: { readonly point: Point }) { const updatedAisles = getAislesByPointId(point.pointUuid); if (updatedAisles.length > 0 && projectId) { updatedAisles.forEach((updatedAisle) => { - createAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || '') + upsertAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || '') }) } } else if (point.pointType === 'Wall') { - // console.log('Wall after drag: ', point); + const updatedWalls = getWallsByPointId(point.pointUuid); + if (updatedWalls && updatedWalls.length > 0 && projectId) { + updatedWalls.forEach((updatedWall) => { + + // API + + // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall); + + // SOCKET + + const data = { + wallData: updatedWall, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + }); + } + } else if (point.pointType === 'Floor') { + const updatedFloors = getFloorsByPointId(point.pointUuid); + if (updatedFloors && updatedFloors.length > 0 && projectId) { + updatedFloors.forEach((updatedFloor) => { + + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor); + + // SOCKET + + const data = { + floorData: updatedFloor, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + }); + } + } else if (point.pointType === 'Zone') { + const updatedZones = getZonesByPointId(point.pointUuid); + if (updatedZones && updatedZones.length > 0 && projectId) { + updatedZones.forEach((updatedZone) => { + + // API + + // upsertZoneApi(projectId, selectedVersion?.versionId || '', updatedZone); + + // SOCKET + + const data = { + zoneData: updatedZone, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:add', data); + }); + } } } @@ -152,16 +237,133 @@ function Point({ point }: { readonly point: Point }) { const removedAisles = removeAislePoint(point.pointUuid); if (removedAisles.length > 0) { removedAisles.forEach(aisle => { - if (projectId) + if (projectId) { deleteAisleApi(aisle.aisleUuid, projectId, selectedVersion?.versionId || '') + } }); setHoveredPoint(null); } } if (point.pointType === 'Wall') { - const removedAisles = removeWallPoint(point.pointUuid); - if (removedAisles.length > 0) { + const removedWalls = removeWallPoint(point.pointUuid); + if (removedWalls.length > 0) { setHoveredPoint(null); + removedWalls.forEach(wall => { + if (projectId) { + + // API + + // deleteWallApi(projectId, selectedVersion?.versionId || '', wall.wallUuid); + + // SOCKET + + const data = { + wallUuid: wall.wallUuid, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:delete', data); + } + }); + } + } + if (point.pointType === 'Floor') { + const { removedFloors, updatedFloors } = removeFloorPoint(point.pointUuid); + setHoveredPoint(null); + if (removedFloors.length > 0) { + removedFloors.forEach(floor => { + if (projectId) { + + // API + + // deleteFloorApi(projectId, selectedVersion?.versionId || '', floor.floorUuid); + + // SOCKET + + const data = { + floorUuid: floor.floorUuid, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:delete', data); + } + }); + } + if (updatedFloors.length > 0) { + updatedFloors.forEach(floor => { + if (projectId) { + + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', floor); + + // SOCKET + + const data = { + floorData: floor, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + } + }); + } + } + if (point.pointType === 'Zone') { + const { removedZones, updatedZones } = removeZonePoint(point.pointUuid); + setHoveredPoint(null); + if (removedZones.length > 0) { + removedZones.forEach(zone => { + if (projectId) { + + // API + + // deleteZoneApi(projectId, selectedVersion?.versionId || '', zone.zoneUuid); + + // SOCKET + + const data = { + zoneUuid: zone.zoneUuid, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:delete', data); + } + }); + } + if (updatedZones.length > 0) { + updatedZones.forEach(zone => { + if (projectId) { + + // API + + // upsertZoneApi(projectId, selectedVersion?.versionId || '', zone); + + // SOCKET + + const data = { + zoneData: zone, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:add', data); + } + }); } } gl.domElement.style.cursor = 'default'; diff --git a/app/src/modules/builder/point/reference/referencePoint.tsx b/app/src/modules/builder/point/reference/referencePoint.tsx index 44d399f..2f2cccd 100644 --- a/app/src/modules/builder/point/reference/referencePoint.tsx +++ b/app/src/modules/builder/point/reference/referencePoint.tsx @@ -21,9 +21,17 @@ function ReferencePoint({ point }: { readonly point: Point }) { return null; } + const pointName = point.pointType === 'Wall' ? 'Wall-Point' : + point.pointType === 'Floor' ? 'Floor-Point' : + point.pointType === 'Aisle' ? 'Aisle-Point' : + point.pointType === 'Zone' ? 'Zone-Point' : 'Point'; + return ( ls.geometry.coordinates); + const uniqueCoords = Array.from(new Set(allCoords.map(coord => coord.join(',')))); + if (uniqueCoords.length < 4) return []; + const lineStrings = turf.featureCollection(mergedLineStrings); - // Now polygonize merged line strings const polygons = turf.polygonize(lineStrings); const rooms: Point[][] = []; diff --git a/app/src/modules/builder/wall/Instances/instance/wall.tsx b/app/src/modules/builder/wall/Instances/instance/wall.tsx index 037816b..280167d 100644 --- a/app/src/modules/builder/wall/Instances/instance/wall.tsx +++ b/app/src/modules/builder/wall/Instances/instance/wall.tsx @@ -1,20 +1,21 @@ import * as THREE from 'three'; import { Base } from '@react-three/csg'; import { useMemo, useRef, useState } from 'react'; -import { Decal, MeshDiscardMaterial } from '@react-three/drei'; +import { MeshDiscardMaterial } from '@react-three/drei'; import { useFrame, useThree } from '@react-three/fiber'; - -import defaultMaterial from '../../../../../assets/textures/floor/wall-tex.png'; -import material1 from '../../../../../assets/textures/floor/factory wall texture.jpg'; - import useModuleStore from '../../../../../store/useModuleStore'; import { useSceneContext } from '../../../../scene/sceneContext'; import { useWallClassification } from './helpers/useWallClassification'; import { useToggleView, useWallVisibility } from '../../../../../store/builder/store'; import { useBuilderStore } from '../../../../../store/builder/useBuilderStore'; import * as Constants from '../../../../../types/world/worldConstants'; + import DecalInstance from '../../../Decal/decalInstance'; +import defaultMaterial from '../../../../../assets/textures/floor/wall-tex.png'; +import material1 from '../../../../../assets/textures/floor/factory wall texture.jpg'; + + function Wall({ wall }: { readonly wall: Wall }) { const { wallStore } = useSceneContext(); const { walls, addDecal } = wallStore(); @@ -116,14 +117,16 @@ function Wall({ wall }: { readonly wall: Wall }) { { + onDoubleClick={(e) => { if (visible && !togglView && activeModule === 'builder') { if (e.object.userData.wallUuid) { + e.stopPropagation(); setSelectedWall(e.object); setSelectedDecal(null); @@ -151,7 +154,7 @@ function Wall({ wall }: { readonly wall: Wall }) { {wall.decals.map((decal) => ( - + ))} diff --git a/app/src/modules/builder/wall/Instances/wallInstances.tsx b/app/src/modules/builder/wall/Instances/wallInstances.tsx index 4d5da1e..c5e22bf 100644 --- a/app/src/modules/builder/wall/Instances/wallInstances.tsx +++ b/app/src/modules/builder/wall/Instances/wallInstances.tsx @@ -1,6 +1,5 @@ import React, { useEffect, useMemo } from 'react'; import { DoubleSide, RepeatWrapping, Shape, SRGBColorSpace, TextureLoader, Vector2, Vector3 } from 'three'; -import { Geometry } from '@react-three/csg'; import { Html, Extrude } from '@react-three/drei'; import { useLoader } from '@react-three/fiber'; import { useSceneContext } from '../../../scene/sceneContext'; @@ -17,7 +16,7 @@ import texturePathDark from "../../../../assets/textures/floor/black.png"; function WallInstances() { const { wallStore } = useSceneContext(); const { walls } = wallStore(); - const { rooms } = useWallClassification(walls) + const { rooms } = useWallClassification(walls); const { toggleView } = useToggleView(); useEffect(() => { @@ -28,8 +27,8 @@ function WallInstances() { const points: Point[] = []; const seenUuids = new Set(); - walls.forEach(aisle => { - aisle.points.forEach(point => { + walls.forEach(wall => { + wall.points.forEach(point => { if (!seenUuids.has(point.pointUuid)) { seenUuids.add(point.pointUuid); points.push(point); @@ -44,13 +43,9 @@ function WallInstances() { <> {!toggleView && walls.length > 1 && ( <> - - - {walls.map((wall) => ( - - ))} - - + {walls.map((wall) => ( + + ))} {rooms.map((room, index) => ( @@ -134,14 +129,15 @@ function Floor({ room }: { room: Point[] }) { if (!shape) return null; return ( - + - + ); } diff --git a/app/src/modules/builder/wall/wallCreator/referenceWall.tsx b/app/src/modules/builder/wall/wallCreator/referenceWall.tsx index f932f46..905c60d 100644 --- a/app/src/modules/builder/wall/wallCreator/referenceWall.tsx +++ b/app/src/modules/builder/wall/wallCreator/referenceWall.tsx @@ -36,46 +36,45 @@ function ReferenceWall({ tempPoints }: Readonly) { setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]); - if (intersectionPoint) { - const snapped = snapWallPoint([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; - - const wallPoints: [Point, Point] = [ - tempPoints[0], - { - pointUuid: 'temp-point', - pointType: 'Wall', - position: finalPosition.current, - layer: activeLayer, - } - ]; - - setTempWall({ - wallUuid: 'temp-wall', - points: wallPoints, - outsideMaterial: 'default', - insideMaterial: 'default', - wallThickness: wallThickness, - wallHeight: wallHeight, - decals: [] - }) + if (!intersectionPoint) return; + const snapped = snapWallPoint([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z], tempPoints[0]); + 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; + + const wallPoints: [Point, Point] = [ + tempPoints[0], + { + pointUuid: 'temp-point', + pointType: 'Wall', + position: finalPosition.current, + layer: activeLayer, + } + ]; + + setTempWall({ + wallUuid: 'temp-wall', + points: wallPoints, + outsideMaterial: 'default', + insideMaterial: 'default', + wallThickness: wallThickness, + wallHeight: wallHeight, + decals: [] + }) + } else if (tempWall !== null) { setTempWall(null); } @@ -83,7 +82,7 @@ function ReferenceWall({ tempPoints }: Readonly) { useEffect(() => { setTempWall(null); - }, [toolMode, toggleView, tempPoints.length, wallHeight, wallThickness]); + }, [toolMode, toggleView, tempPoints.length, wallHeight, wallThickness, activeLayer]); if (!tempWall) return null; diff --git a/app/src/modules/builder/wall/wallCreator/wallCreator.tsx b/app/src/modules/builder/wall/wallCreator/wallCreator.tsx index a136a41..a80a62d 100644 --- a/app/src/modules/builder/wall/wallCreator/wallCreator.tsx +++ b/app/src/modules/builder/wall/wallCreator/wallCreator.tsx @@ -5,9 +5,15 @@ import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../. import * as Constants from '../../../../types/world/worldConstants'; import { useSceneContext } from '../../../scene/sceneContext'; import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; +import { useParams } from 'react-router-dom'; +import { useVersionContext } from '../../version/versionContext'; +import { getUserData } from '../../../../functions/getUserData'; +import getClosestIntersection from '../../line/helpers/getClosestIntersection'; import ReferencePoint from '../../point/reference/referencePoint'; import ReferenceWall from './referenceWall'; -import getClosestIntersection from '../../geomentries/lines/getClosestIntersection'; + +// import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi'; +// import { deleteWallApi } from '../../../../services/factoryBuilder/wall/deleteWallApi'; function WallCreator() { const { scene, camera, raycaster, gl, pointer } = useThree(); @@ -20,10 +26,14 @@ function WallCreator() { const { addWall, getWallPointById, removeWall, getWallByPoints } = wallStore(); const drag = useRef(false); const isLeftMouseDown = useRef(false); - const { wallThickness, wallHeight, insideMaterial, outsideMaterial, snappedPosition, snappedPoint } = useBuilderStore(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { userId, organization } = getUserData(); + const { projectId } = useParams(); const [tempPoints, setTempPoints] = useState([]); const [isCreating, setIsCreating] = useState(false); + const { wallThickness, wallHeight, insideMaterial, outsideMaterial, snappedPosition, snappedPoint, setSnappedPoint, setSnappedPosition } = useBuilderStore(); useEffect(() => { const canvasElement = gl.domElement; @@ -58,6 +68,7 @@ function WallCreator() { const pointIntersects = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Wall-Point'); const wallIntersect = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Wall-Line'); + if (wallIntersect && !pointIntersects) { const wall = getWallByPoints(wallIntersect.object.userData.points); if (wall) { @@ -80,6 +91,25 @@ function WallCreator() { const closestPoint = new THREE.Vector3().lerpVectors(point1Vec, point2Vec, t); removeWall(wall.wallUuid); + if (projectId) { + + // API + + // deleteWallApi(projectId, selectedVersion?.versionId || '', wall.wallUuid); + + // SOCKET + + const data = { + wallUuid: wall.wallUuid, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:delete', data); + + } const point1: Point = { pointUuid: wall.points[0].pointUuid, @@ -114,6 +144,24 @@ function WallCreator() { } addWall(wall2); + // API + + // if (projectId) { + // upsertWallApi(projectId, selectedVersion?.versionId || '', wall2); + // } + + // SOCKET + + const data = { + wallData: wall2, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + const wall3: Wall = { wallUuid: THREE.MathUtils.generateUUID(), points: [point2, newPoint], @@ -125,6 +173,24 @@ function WallCreator() { } addWall(wall3); + // API + + // if (projectId) { + // upsertWallApi(projectId, selectedVersion?.versionId || '', wall3); + // } + + // SOCKET + + const data2 = { + wallData: wall3, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data2); + setTempPoints([newPoint]); setIsCreating(true); } else { @@ -139,6 +205,24 @@ function WallCreator() { }; addWall(wall1); + // API + + // if (projectId) { + // upsertWallApi(projectId, selectedVersion?.versionId || '', wall1); + // } + + // SOCKET + + const data = { + wallData: wall1, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + const wall2: Wall = { wallUuid: THREE.MathUtils.generateUUID(), points: [point1, newPoint], @@ -150,6 +234,24 @@ function WallCreator() { } addWall(wall2); + // API + + // if (projectId) { + // upsertWallApi(projectId, selectedVersion?.versionId || '', wall2); + // } + + // SOCKET + + const data1 = { + wallData: wall2, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data1); + const wall3: Wall = { wallUuid: THREE.MathUtils.generateUUID(), points: [point2, newPoint], @@ -161,6 +263,24 @@ function WallCreator() { } addWall(wall3); + // API + + // if (projectId) { + // upsertWallApi(projectId, selectedVersion?.versionId || '', wall3); + // } + + // SOCKET + + const data3 = { + wallData: wall3, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data3); + setTempPoints([newPoint]); } @@ -181,6 +301,8 @@ function WallCreator() { newPoint.layer = snappedPoint.layer; } + if (snappedPoint && snappedPoint.pointUuid === tempPoints[0]?.pointUuid) { return } + if (snappedPosition && !snappedPoint) { newPoint.position = snappedPosition; } @@ -208,6 +330,25 @@ function WallCreator() { decals: [] }; addWall(wall); + + // API + + // if (projectId) { + // upsertWallApi(projectId, selectedVersion?.versionId || '', wall); + // } + + // SOCKET + + const data = { + wallData: wall, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + setTempPoints([newPoint]); } @@ -222,6 +363,10 @@ function WallCreator() { }; if (toolMode === "Wall" && toggleView) { + if (tempPoints.length === 0) { + setSnappedPosition(null); + setSnappedPoint(null); + } canvasElement.addEventListener("mousedown", onMouseDown); canvasElement.addEventListener("mouseup", onMouseUp); canvasElement.addEventListener("mousemove", onMouseMove); @@ -244,7 +389,7 @@ function WallCreator() { canvasElement.removeEventListener("click", onMouseClick); canvasElement.removeEventListener("contextmenu", onContext); }; - }, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addWall, getWallPointById, wallThickness, wallHeight, insideMaterial, outsideMaterial, snappedPosition, snappedPoint]); + }, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addWall, getWallPointById, wallThickness, wallHeight, insideMaterial, outsideMaterial, snappedPosition, snappedPoint, selectedVersion?.versionId]); return ( <> diff --git a/app/src/modules/builder/wall/wallGroup.tsx b/app/src/modules/builder/wall/wallGroup.tsx index aae36b9..084b969 100644 --- a/app/src/modules/builder/wall/wallGroup.tsx +++ b/app/src/modules/builder/wall/wallGroup.tsx @@ -1,21 +1,46 @@ import { useEffect } from 'react'; -import { useToggleView } from '../../../store/builder/store' +import { useActiveTool, useToggleView } from '../../../store/builder/store'; import { useBuilderStore } from '../../../store/builder/useBuilderStore'; -import WallCreator from './wallCreator/wallCreator' -import WallInstances from './Instances/wallInstances' +import { useVersionContext } from '../version/versionContext'; +import { useSceneContext } from '../../scene/sceneContext'; +import { useParams } from 'react-router-dom'; import useModuleStore from '../../../store/useModuleStore'; +import WallCreator from './wallCreator/wallCreator'; +import WallInstances from './Instances/wallInstances'; + +import { getWallsApi } from '../../../services/factoryBuilder/wall/getWallsApi'; function WallGroup() { const { togglView } = useToggleView(); const { setSelectedWall, setSelectedDecal } = useBuilderStore(); const { activeModule } = useModuleStore(); + const { activeTool } = useActiveTool(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { wallStore } = useSceneContext(); + const { setWalls } = wallStore(); + const { projectId } = useParams(); useEffect(() => { if (togglView || activeModule !== 'builder') { setSelectedWall(null); setSelectedDecal(null); } - }, [togglView, activeModule]) + }, [togglView, activeModule, activeTool]) + + useEffect(() => { + if (projectId && selectedVersion) { + getWallsApi(projectId, selectedVersion?.versionId || '').then((walls) => { + if (walls && walls.length > 0) { + setWalls(walls); + } else { + setWalls([]); + } + }).catch((err) => { + console.log(err); + }) + } + }, [projectId, selectedVersion?.versionId]) return ( <> diff --git a/app/src/modules/builder/wallAsset/Instances/Instances/wallAssetInstance.tsx b/app/src/modules/builder/wallAsset/Instances/Instances/wallAssetInstance.tsx new file mode 100644 index 0000000..d9f8df0 --- /dev/null +++ b/app/src/modules/builder/wallAsset/Instances/Instances/wallAssetInstance.tsx @@ -0,0 +1,261 @@ +import * as THREE from 'three'; +import { useCallback, useEffect, useMemo, 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 { Base, Geometry, Subtraction } from '@react-three/csg'; +import useModuleStore from '../../../../../store/useModuleStore'; +import { useSceneContext } from '../../../../scene/sceneContext'; +import { useBuilderStore } from '../../../../../store/builder/useBuilderStore'; +import { useActiveTool, useToggleView } from '../../../../../store/builder/store'; +import closestPointOnLineSegment from '../../../line/helpers/getClosestPointOnLineSegment'; +import { useThree } from '@react-three/fiber'; + +function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) { + const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; + const { raycaster, pointer, camera, scene, controls, gl } = useThree(); + const { wallStore, wallAssetStore } = useSceneContext(); + const { walls, getWallById } = wallStore(); + const { updateWallAsset, removeWallAsset } = wallAssetStore(); + const { toggleView } = useToggleView(); + const { activeTool } = useActiveTool(); + const { activeModule } = useModuleStore(); + const { selectedWallAsset, setSelectedWallAsset, setDeletableWallAsset, deletableWallAsset } = useBuilderStore(); + const [gltfScene, setGltfScene] = useState(null); + const [boundingBox, setBoundingBox] = useState(null); + const groupRef = useRef(null); + const wall = useMemo(() => getWallById(wallAsset.wallUuid), [getWallById, wallAsset.wallUuid, walls]); + const draggingRef = useRef(false); + + 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 = wallAsset.assetId; + const cachedModel = THREE.Cache.get(assetId); + if (cachedModel) { + setGltfScene(cachedModel.scene.clone()); + 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.scene.clone()); + calculateBoundingBox(gltf.scene); + }, + undefined, + (error) => { + echo.error(`[IndexedDB] Error loading ${wallAsset.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.scene.clone()); + calculateBoundingBox(gltf.scene); + } catch (error) { + console.error(`[Backend] Error storing/loading ${wallAsset.modelName}:`, error); + } + }; + loader.load( + modelUrl, + handleBackendLoad, + undefined, + (error) => { + echo.error(`[Backend] Error loading ${wallAsset.modelName}:`); + } + ); + } catch (err) { + console.error("Failed to load model:", wallAsset.assetId, err); + } + }; + + const calculateBoundingBox = (scene: THREE.Object3D) => { + const box = new THREE.Box3().setFromObject(scene); + setBoundingBox(box); + }; + + loadModel(); + + }, []); + + useEffect(() => { + const canvasElement = gl.domElement; + + const onPointerUp = () => { + draggingRef.current = false; + if (controls) { + (controls as any).enabled = true; + } + }; + + const onPointerMove = (e: any) => { + if (!draggingRef.current || !wall || !selectedWallAsset) return; + if (controls) { + (controls as any).enabled = false; + } + pointer.x = (e.clientX / window.innerWidth) * 2 - 1; + pointer.y = -(e.clientY / window.innerHeight) * 2 + 1; + + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster.intersectObjects(scene.children, true); + const intersect = intersects.find((i: any) => i.object.name.includes('WallReference')); + + if (intersect && intersect.object.userData.wallUuid) { + const newPoint = closestPointOnLineSegment( + new THREE.Vector3(intersect.point.x, 0, intersect.point.z), + new THREE.Vector3(...intersect.object.userData.points[0].position), + new THREE.Vector3(...intersect.object.userData.points[1].position) + ); + + const wallRotation = intersect.object.rotation.clone(); + + updateWallAsset(wallAsset.modelUuid, { + wallUuid: intersect.object.userData.wallUuid, + position: [newPoint.x, wallAsset.wallAssetType === 'fixed-move' ? 0 : intersect.point.y, newPoint.z], + rotation: [wallRotation.x, wallRotation.y, wallRotation.z], + }); + } + }; + + if (selectedWallAsset && !toggleView && activeModule === 'builder') { + canvasElement.addEventListener('mousemove', onPointerMove); + canvasElement.addEventListener('pointerup', onPointerUp); + } + + return () => { + canvasElement.removeEventListener('mousemove', onPointerMove); + canvasElement.removeEventListener('pointerup', onPointerUp); + }; + + }, [gl, camera, toggleView, activeModule, selectedWallAsset]) + + const handlePointerClick = useCallback((wallAsset: WallAsset) => { + if (activeTool === 'delete' && deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) { + removeWallAsset(wallAsset.modelUuid); + } + }, [activeTool, activeModule, deletableWallAsset]) + + const handlePointerOver = useCallback((wallAsset: WallAsset, currentObject: THREE.Object3D) => { + if (activeTool === "delete" && activeModule === 'builder') { + if (deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) { + return; + } else { + setDeletableWallAsset(currentObject); + } + } + }, [activeTool, activeModule, deletableWallAsset]); + + const handlePointerOut = useCallback((wallAsset: WallAsset) => { + if (activeTool === "delete" && deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) { + setDeletableWallAsset(null); + } + }, [activeTool, deletableWallAsset]); + + if (!gltfScene || !boundingBox || !wall) { return null } + const size = new THREE.Vector3(); + boundingBox.getSize(size); + const center = new THREE.Vector3(); + boundingBox.getCenter(center); + + return ( + <> + + + + + + + {gltfScene && ( + { + if (!toggleView && activeModule === 'builder' && selectedWallAsset && selectedWallAsset.userData.modelUuid === wallAsset.modelUuid) { + draggingRef.current = true; + e.stopPropagation(); + setSelectedWallAsset(groupRef.current); + if (controls) { + (controls as any).enabled = false; + } + } + }} + onClick={(e) => { + if (!toggleView && activeModule === 'builder' && activeTool !== 'delete') { + if (e.object) { + e.stopPropagation(); + let currentObject = e.object as THREE.Object3D; + while (currentObject) { + if (currentObject.name === "Scene") { + break; + } + currentObject = currentObject.parent as THREE.Object3D; + } + setSelectedWallAsset(currentObject); + } + } else if (!toggleView && activeModule === 'builder' && activeTool === 'delete') { + handlePointerClick(wallAsset); + } + }} + onPointerMissed={() => { + if (selectedWallAsset && selectedWallAsset.userData.modelUuid === wallAsset.modelUuid) { + setSelectedWallAsset(null); + } + }} + onPointerEnter={(e) => { + if (!toggleView) { + e.stopPropagation(); + let currentObject = e.object as THREE.Object3D; + while (currentObject) { + if (currentObject.name === "Scene") { + break; + } + currentObject = currentObject.parent as THREE.Object3D; + } + handlePointerOver(wallAsset, currentObject); + } + }} + onPointerOut={(e) => { + if (!toggleView) { + e.stopPropagation(); + handlePointerOut(wallAsset); + } + }} + userData={wallAsset} + > + + + )} + + + ) +} + +export default WallAssetInstance \ No newline at end of file diff --git a/app/src/modules/builder/wallAsset/Instances/wallAssetInstances.tsx b/app/src/modules/builder/wallAsset/Instances/wallAssetInstances.tsx new file mode 100644 index 0000000..581dfe7 --- /dev/null +++ b/app/src/modules/builder/wallAsset/Instances/wallAssetInstances.tsx @@ -0,0 +1,30 @@ +import { useEffect } from 'react'; +import { useSceneContext } from '../../../scene/sceneContext' +import { useToggleView } from '../../../../store/builder/store'; +import WallAssetInstance from './Instances/wallAssetInstance'; + +function WallAssetInstances() { + const { wallAssetStore } = useSceneContext(); + const { wallAssets } = wallAssetStore(); + const { toggleView } = useToggleView(); + + useEffect(() => { + // console.log('wallAssets: ', wallAssets); + }, [wallAssets]) + + return ( + <> + + {!toggleView && wallAssets.length > 0 && ( + <> + {wallAssets.map((wallAsset) => ( + + ))} + + )} + + + ) +} + +export default WallAssetInstances \ No newline at end of file diff --git a/app/src/modules/builder/wallAsset/wallAssetCreator.tsx b/app/src/modules/builder/wallAsset/wallAssetCreator.tsx new file mode 100644 index 0000000..aacca0e --- /dev/null +++ b/app/src/modules/builder/wallAsset/wallAssetCreator.tsx @@ -0,0 +1,75 @@ +import { useThree } from '@react-three/fiber'; +import { useEffect } from 'react' +import { useSelectedItem, useSocketStore, useToggleView } from '../../../store/builder/store'; +import useModuleStore from '../../../store/useModuleStore'; +import { MathUtils, Vector3 } from 'three'; +import { useSceneContext } from '../../scene/sceneContext'; +import closestPointOnLineSegment from '../line/helpers/getClosestPointOnLineSegment'; + +function WallAssetCreator() { + const { socket } = useSocketStore(); + const { pointer, camera, raycaster, scene, gl } = useThree(); + const { togglView } = useToggleView(); + const { activeModule } = useModuleStore(); + const { wallAssetStore } = useSceneContext(); + const { addWallAsset } = wallAssetStore(); + const { selectedItem, setSelectedItem } = useSelectedItem(); + + useEffect(() => { + const canvasElement = gl.domElement; + + const onDrop = (event: DragEvent) => { + 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; + + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster.intersectObjects(scene.children, true); + const intersect = intersects.find((intersect) => intersect.object.name.includes('WallReference')); + + if (intersect) { + const wall = intersect.object.userData as Wall; + const closestPoint = closestPointOnLineSegment( + new Vector3(intersect.point.x, 0, intersect.point.z), + new Vector3(...wall.points[0].position), + new Vector3(...wall.points[1].position) + ) + + const wallRotation = intersect.object.rotation.clone(); + + const newWallAsset: WallAsset = { + modelName: selectedItem.name, + modelUuid: MathUtils.generateUUID(), + wallUuid: wall.wallUuid, + wallAssetType: selectedItem.subCategory, + assetId: selectedItem.id, + position: [closestPoint.x, selectedItem.subCategory === "fixed-move" ? 0 : intersect.point.y, closestPoint.z], + rotation: [wallRotation.x, wallRotation.y, wallRotation.z], + isLocked: false, + isVisible: true, + opacity: 1, + }; + + addWallAsset(newWallAsset); + } + } + }; + + if (!togglView && activeModule === 'builder') { + canvasElement.addEventListener('drop', onDrop); + } + + return () => { + canvasElement.removeEventListener('drop', onDrop); + }; + + }, [gl, camera, togglView, activeModule, socket, selectedItem, setSelectedItem]); + + return ( + <> + + ) +} + +export default WallAssetCreator \ No newline at end of file diff --git a/app/src/modules/builder/wallAsset/wallAssetGroup.tsx b/app/src/modules/builder/wallAsset/wallAssetGroup.tsx new file mode 100644 index 0000000..f9d1290 --- /dev/null +++ b/app/src/modules/builder/wallAsset/wallAssetGroup.tsx @@ -0,0 +1,40 @@ +import { useEffect } from 'react'; +import { useActiveTool, useToggleView } from '../../../store/builder/store'; +import { useBuilderStore } from '../../../store/builder/useBuilderStore'; +import { useVersionContext } from '../version/versionContext'; +import { useSceneContext } from '../../scene/sceneContext'; +import { useParams } from 'react-router-dom'; +import useModuleStore from '../../../store/useModuleStore'; +import WallAssetCreator from './wallAssetCreator' +import WallAssetInstances from './Instances/wallAssetInstances' + +function WallAssetGroup() { + const { togglView } = useToggleView(); + const { setSelectedFloorAsset, setDeletableWallAsset } = useBuilderStore(); + const { activeModule } = useModuleStore(); + const { activeTool } = useActiveTool(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { wallAssetStore } = useSceneContext(); + const { setWallAssets } = wallAssetStore(); + const { projectId } = useParams(); + + useEffect(() => { + if (togglView || activeModule !== 'builder') { + setSelectedFloorAsset(null); + } + setDeletableWallAsset(null); + }, [togglView, activeModule, activeTool]) + + return ( + <> + + + + + + + ) +} + +export default WallAssetGroup \ No newline at end of file diff --git a/app/src/modules/builder/zone/Instances/Instance/zone2DInstance.tsx b/app/src/modules/builder/zone/Instances/Instance/zone2DInstance.tsx new file mode 100644 index 0000000..3489b32 --- /dev/null +++ b/app/src/modules/builder/zone/Instances/Instance/zone2DInstance.tsx @@ -0,0 +1,50 @@ +import { useMemo } from 'react'; +import { DoubleSide, Shape, Vector2 } from 'three'; +import { Extrude } from '@react-three/drei'; +import * as Constants from '../../../../../types/world/worldConstants'; + +function Zone2DInstance({ zone }: { zone: Zone }) { + const savedTheme: string | null = localStorage.getItem("theme"); + + const shape = useMemo(() => { + const shape = new Shape(); + const points = zone.points.map(p => new Vector2(p.position[0], p.position[2])); + if (points.length < 3) return null; + shape.moveTo(points[0].x, points[0].y); + for (let i = 1; i < points.length; i++) { + shape.lineTo(points[i].x, points[i].y); + } + return shape; + }, [zone]); + + if (!shape) return null; + + return ( + + + + + + ); +} + +export default Zone2DInstance; \ No newline at end of file diff --git a/app/src/modules/builder/zone/Instances/Instance/zoneInstance.tsx b/app/src/modules/builder/zone/Instances/Instance/zoneInstance.tsx new file mode 100644 index 0000000..e49400c --- /dev/null +++ b/app/src/modules/builder/zone/Instances/Instance/zoneInstance.tsx @@ -0,0 +1,73 @@ +import { useMemo } from 'react' +import { Color, DoubleSide, ShaderMaterial } from 'three'; +import { Vector3 } from 'three'; + +function ZoneInstance({ zone }: { zone: Zone }) { + const zoneLayer = zone.points[0].layer; + + const zoneMaterial = useMemo(() => { + return new ShaderMaterial({ + side: DoubleSide, + vertexShader: ` + varying vec2 vUv; + void main(){ + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + vUv = uv; + } + `, + fragmentShader: ` + varying vec2 vUv; + uniform vec3 uOuterColor; + void main(){ + float alpha = 1.0 - vUv.y; + gl_FragColor = vec4(uOuterColor, alpha); + } + `, + uniforms: { + uOuterColor: { value: new Color(zone.zoneColor) }, + }, + transparent: true, + depthWrite: false, + }) + }, []); + + return ( + <> + + {zone.points.map((point, index: number) => { + const nextPoint = zone.points[(index + 1) % zone.points.length]; + + const point1 = new Vector3(point.position[0], point.position[1], point.position[2]); + const point2 = new Vector3(nextPoint.position[0], nextPoint.position[1], nextPoint.position[2]); + + const planeWidth = point1.distanceTo(point2); + const planeHeight = zone.zoneHeight; + + const midpoint = new Vector3((point1.x + point2.x) / 2, zone.zoneHeight / 2 + (zoneLayer - 1) * zone.zoneHeight, (point1.z + point2.z) / 2); + + const angle = Math.atan2(point2.z - point1.z, point2.x - point1.x); + + return ( + + + + + ); + })} + + + + ) +} + +export default ZoneInstance \ No newline at end of file diff --git a/app/src/modules/builder/zone/Instances/zoneInstances.tsx b/app/src/modules/builder/zone/Instances/zoneInstances.tsx new file mode 100644 index 0000000..39a60a7 --- /dev/null +++ b/app/src/modules/builder/zone/Instances/zoneInstances.tsx @@ -0,0 +1,132 @@ +import React, { useEffect, useMemo } from 'react'; +import { Vector3 } from 'three'; +import { Html } from '@react-three/drei'; +import { useSceneContext } from '../../../scene/sceneContext'; +import { useToggleView } from '../../../../store/builder/store'; +import Line from '../../line/line'; +import Point from '../../point/point'; +import ZoneInstance from './Instance/zoneInstance'; +import Zone2DInstance from './Instance/zone2DInstance'; + +function ZoneInstances() { + const { zoneStore } = useSceneContext(); + const { zones } = zoneStore(); + const { toggleView } = useToggleView(); + + useEffect(() => { + // console.log('zones: ', zones); + }, [zones]) + + const allPoints = useMemo(() => { + const points: Point[] = []; + const seenUuids = new Set(); + + zones.forEach(zone => { + zone.points.forEach(point => { + if (!seenUuids.has(point.pointUuid)) { + seenUuids.add(point.pointUuid); + points.push(point); + } + }); + }); + + return points; + }, [zones]); + + const allLines = useMemo(() => { + const lines: { start: Point; end: Point; key: string }[] = []; + const seenUuids = new Set(); + + zones.forEach((zone) => { + const points = zone.points; + if (points.length < 2) return; + + for (let i = 0; i < points.length; i++) { + const current = points[i]; + const next = points[(i + 1) % points.length]; + const lineKey = `${current.pointUuid}-${next.pointUuid}`; + if (current.pointUuid !== next.pointUuid && !seenUuids.has(lineKey)) { + seenUuids.add(lineKey); + lines.push({ + start: current, + end: next, + key: lineKey + }); + } + } + }); + + return lines; + }, [zones]); + + return ( + <> + + {!toggleView && zones.length > 0 && ( + + {zones.map((zone) => ( + + ))} + + )} + + {toggleView && zones.length > 0 && ( + + {zones.map((zone) => ( + + ))} + + )} + + {toggleView && ( + <> + + {allPoints.map((point) => ( + + ))} + + + + + {allLines.map(({ start, end, key }) => ( + + ))} + + {allLines.map((line) => { + const { start, end, key } = line; + const textPosition = new Vector3().addVectors(new Vector3(...start.position), new Vector3(...end.position)).divideScalar(2); + const distance = new Vector3(...start.position).distanceTo(new Vector3(...end.position)); + + return ( + + {toggleView && + +
+ {distance.toFixed(2)} m +
+ + } +
+ ) + })} + +
+ + )} + + ) +} + +export default ZoneInstances \ No newline at end of file diff --git a/app/src/modules/builder/zone/zoneCreator/referenceZone.tsx b/app/src/modules/builder/zone/zoneCreator/referenceZone.tsx new file mode 100644 index 0000000..e5cdf9f --- /dev/null +++ b/app/src/modules/builder/zone/zoneCreator/referenceZone.tsx @@ -0,0 +1,163 @@ +import { useEffect, useRef, useState, useMemo } from 'react'; +import * as THREE from 'three'; +import { useFrame, useThree } from '@react-three/fiber'; +import { Extrude, Html } from '@react-three/drei'; +import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; +import { useActiveLayer, useToolMode, useToggleView } from '../../../../store/builder/store'; +import { useDirectionalSnapping } from '../../point/helpers/useDirectionalSnapping'; +import { usePointSnapping } from '../../point/helpers/usePointSnapping'; +import ReferenceLine from '../../line/reference/referenceLine'; + +interface ReferenceZoneProps { + tempPoints: Point[]; +} + +function ReferenceZone({ tempPoints }: Readonly) { + const { zoneColor, zoneHeight, 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 [tempZone, setTempZone] = useState(null); + const [currentPosition, setCurrentPosition] = useState<[number, number, number]>(tempPoints[0]?.position); + + const directionalSnap = useDirectionalSnapping(currentPosition, tempPoints[tempPoints.length - 1]?.position || null); + const { snapZonePoint } = usePointSnapping({ uuid: 'temp-Zone', pointType: 'Zone', position: directionalSnap.position || [0, 0, 0], }); + + useFrame(() => { + if (toolMode === 'Zone' && toggleView && tempPoints.length > 0) { + raycaster.setFromCamera(pointer, camera); + const intersectionPoint = new THREE.Vector3(); + raycaster.ray.intersectPlane(plane, intersectionPoint); + + setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]); + + if (!intersectionPoint) return; + const snapped = snapZonePoint([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z], tempPoints.length > 2 ? [tempPoints[0]] : []); + + 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; + + const zonePoints: Point[] = [ + ...tempPoints, + { + pointUuid: 'temp-point', + pointType: 'Zone', + position: finalPosition.current, + layer: activeLayer, + }, + ]; + + setTempZone({ + zoneUuid: 'temp-zone', + zoneName: 'temp-zone', + points: zonePoints, + zoneColor, + zoneHeight, + viewPortPosition: [0, 0, 0], + viewPortTarget: [0, 0, 0], + }); + + } else if (tempZone !== null) { + setTempZone(null); + } + }); + + useEffect(() => { + setTempZone(null); + }, [toolMode, toggleView, tempPoints.length, zoneColor, zoneHeight, activeLayer]); + + const allLines = useMemo(() => { + if (!tempZone || tempZone.points.length < 2) return []; + + const lines: [Point, Point][] = []; + const pts = tempZone.points; + + for (let i = 0; i < pts.length - 1; i++) { + lines.push([pts[i], pts[i + 1]]); + } + + return lines; + }, [tempZone]); + + if (!tempZone) return null; + + return ( + + {allLines.map(([p1, p2], idx) => { + const v1 = new THREE.Vector3(...p1.position); + const v2 = new THREE.Vector3(...p2.position); + const mid = new THREE.Vector3().addVectors(v1, v2).multiplyScalar(0.5); + const dist = v1.distanceTo(v2); + + return ( + + + {toggleView && ( + +
{dist.toFixed(2)} m
+ + )} +
+ ); + })} + + {tempPoints.length >= 3 && ( + + )} +
+ ); +} + +export default ReferenceZone; + + +function Zone({ zone }: { zone: Point[] }) { + const savedTheme: string | null = localStorage.getItem('theme'); + + const shape = useMemo(() => { + const shape = new THREE.Shape(); + const points = zone.map(p => new THREE.Vector2(p.position[0], p.position[2])); + if (points.length < 3) return null; + shape.moveTo(points[0].x, points[0].y); + points.forEach((pt) => { shape.lineTo(pt.x, pt.y); }); + return shape; + }, [zone]); + + if (!shape) return null; + + return ( + + + + + + ); +} diff --git a/app/src/modules/builder/zone/zoneCreator/zoneCreator.tsx b/app/src/modules/builder/zone/zoneCreator/zoneCreator.tsx new file mode 100644 index 0000000..1df200f --- /dev/null +++ b/app/src/modules/builder/zone/zoneCreator/zoneCreator.tsx @@ -0,0 +1,261 @@ +import * as THREE from 'three' +import { useEffect, useMemo, useRef, useState } from 'react' +import { useThree } from '@react-three/fiber'; +import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store'; +import { useSceneContext } from '../../../scene/sceneContext'; +import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; +import { useParams } from 'react-router-dom'; +import { useVersionContext } from '../../version/versionContext'; +import { getUserData } from '../../../../functions/getUserData'; +import ReferencePoint from '../../point/reference/referencePoint'; +import ReferenceZone from './referenceZone'; + +// import { upsertZoneApi } from '../../../../services/factoryBuilder/zone/upsertZoneApi'; + +function ZoneCreator() { + 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 { zoneStore } = useSceneContext(); + const { addZone, getZonePointById, getZoneByPoints } = zoneStore(); + const drag = useRef(false); + const isLeftMouseDown = useRef(false); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { userId, organization } = getUserData(); + const { projectId } = useParams(); + + const [tempPoints, setTempPoints] = useState([]); + const [isCreating, setIsCreating] = useState(false); + const { zoneColor, zoneHeight, snappedPosition, snappedPoint, setSnappedPoint, setSnappedPosition } = useBuilderStore(); + + useEffect(() => { + const canvasElement = gl.domElement; + + const onMouseDown = (evt: any) => { + if (evt.button === 0) { + isLeftMouseDown.current = true; + drag.current = false; + } + }; + + const onMouseUp = (evt: any) => { + if (evt.button === 0) { + isLeftMouseDown.current = false; + } + }; + + const onMouseMove = () => { + if (isLeftMouseDown) { + drag.current = true; + } + }; + + const onMouseClick = () => { + if (drag.current || !toggleView) return; + + raycaster.setFromCamera(pointer, camera); + const intersectionPoint = new THREE.Vector3(); + let position = raycaster.ray.intersectPlane(plane, intersectionPoint); + if (!position) return; + + const pointIntersects = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Zone-Point'); + + // const zoneIntersect = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Zone-Line'); + + // if (zoneIntersect && !pointIntersects) { + + // } + + const newPoint: Point = { + pointUuid: THREE.MathUtils.generateUUID(), + pointType: 'Zone', + position: [position.x, position.y, position.z], + layer: activeLayer + }; + + if (snappedPosition && snappedPoint) { + newPoint.pointUuid = snappedPoint.pointUuid; + newPoint.position = snappedPosition; + newPoint.layer = snappedPoint.layer; + } + + if (snappedPoint && snappedPoint.pointUuid === tempPoints[tempPoints.length - 1]?.pointUuid) { return } + + if (snappedPosition && !snappedPoint) { + newPoint.position = snappedPosition; + } + + if (tempPoints.length > 2 && isCreating && snappedPoint && snappedPoint.pointUuid === tempPoints[0].pointUuid) { + const zone: Zone = { + zoneUuid: THREE.MathUtils.generateUUID(), + zoneName: "Zone", + points: tempPoints, + zoneColor, + zoneHeight, + viewPortPosition: [0, 0, 0], + viewPortTarget: [0, 0, 0] + }; + + addZone(zone); + if (projectId) { + + // API + + // upsertZoneApi(projectId, selectedVersion?.versionId || '', zone); + + // SOCKET + + const data = { + zoneData: zone, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:add', data); + + } + setTempPoints([]); + setIsCreating(false); + } else if (isCreating && snappedPoint && !tempPoints.some(p => p.pointUuid === snappedPoint.pointUuid)) { + setTempPoints(prev => [...prev, newPoint]); + setIsCreating(true); + } else if (pointIntersects) { + if (tempPoints.length > 2 && isCreating && pointIntersects.object.uuid === tempPoints[0].pointUuid) { + const zone: Zone = { + zoneUuid: THREE.MathUtils.generateUUID(), + zoneName: "Zone", + points: tempPoints, + zoneColor, + zoneHeight, + viewPortPosition: [0, 0, 0], + viewPortTarget: [0, 0, 0] + }; + + addZone(zone); + if (projectId) { + + // API + + // upsertZoneApi(projectId, selectedVersion?.versionId || '', zone); + + // SOCKET + + const data = { + zoneData: zone, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:add', data); + + } + setTempPoints([]); + setIsCreating(false); + } else if (tempPoints.length === 0 || (tempPoints.length > 1 && !tempPoints.slice(1).some(p => p.pointUuid === pointIntersects.object.uuid))) { + tempPoints.push(pointIntersects.object.userData as Point); + setIsCreating(true); + } + } else { + setTempPoints(prev => [...prev, newPoint]); + setIsCreating(true); + } + + }; + + const onContext = (event: any) => { + event.preventDefault(); + if (isCreating) { + if (tempPoints.length >= 3) { + const zone: Zone = { + zoneUuid: THREE.MathUtils.generateUUID(), + zoneName: "Zone", + points: tempPoints, + zoneColor, + zoneHeight, + viewPortPosition: [0, 0, 0], + viewPortTarget: [0, 0, 0] + }; + + addZone(zone); + if (projectId) { + + // API + + // upsertZoneApi(projectId, selectedVersion?.versionId || '', zone); + + // SOCKET + + const data = { + zoneData: zone, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:add', data); + + } + } + setTempPoints([]); + setIsCreating(false); + } + }; + + if (toolMode === "Zone" && toggleView) { + if (tempPoints.length === 0) { + setSnappedPosition(null); + setSnappedPoint(null); + } + canvasElement.addEventListener("mousedown", onMouseDown); + canvasElement.addEventListener("mouseup", onMouseUp); + canvasElement.addEventListener("mousemove", onMouseMove); + canvasElement.addEventListener("click", onMouseClick); + canvasElement.addEventListener("contextmenu", onContext); + } else { + setTempPoints([]); + setIsCreating(false); + canvasElement.removeEventListener("mousedown", onMouseDown); + canvasElement.removeEventListener("mouseup", onMouseUp); + canvasElement.removeEventListener("mousemove", onMouseMove); + canvasElement.removeEventListener("click", onMouseClick); + canvasElement.removeEventListener("contextmenu", onContext); + } + + 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, addZone, getZonePointById, getZoneByPoints, zoneColor, zoneHeight, snappedPosition, snappedPoint]); + + return ( + <> + {toggleView && + <> + + {tempPoints.map((point) => ( + + ))} + + + {tempPoints.length > 0 && + + } + + } + + ) +} + +export default ZoneCreator \ No newline at end of file diff --git a/app/src/modules/builder/zone/zoneGroup.tsx b/app/src/modules/builder/zone/zoneGroup.tsx new file mode 100644 index 0000000..c44bf09 --- /dev/null +++ b/app/src/modules/builder/zone/zoneGroup.tsx @@ -0,0 +1,55 @@ +import { useEffect } from 'react'; +import { useActiveTool, useToggleView } from '../../../store/builder/store'; +import { useBuilderStore } from '../../../store/builder/useBuilderStore'; +import { useVersionContext } from '../version/versionContext'; +import { useSceneContext } from '../../scene/sceneContext'; +import { useParams } from 'react-router-dom'; +import useModuleStore from '../../../store/useModuleStore'; +import ZoneCreator from './zoneCreator/zoneCreator'; +import ZoneInstances from './Instances/zoneInstances'; + +import { getZonesApi } from '../../../services/factoryBuilder/zone/getZonesApi'; + +function ZoneGroup() { + const { togglView } = useToggleView(); + const { setSelectedZone } = useBuilderStore(); + const { activeModule } = useModuleStore(); + const { activeTool } = useActiveTool(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { zoneStore } = useSceneContext(); + const { setZones } = zoneStore(); + const { projectId } = useParams(); + + useEffect(() => { + if (togglView || activeModule !== 'builder') { + setSelectedZone(null); + } + }, [togglView, activeModule, activeTool]) + + useEffect(() => { + if (projectId && selectedVersion) { + getZonesApi(projectId, selectedVersion?.versionId || '').then((zones) => { + if (zones && zones.length > 0) { + setZones(zones); + } else { + setZones([]); + } + }).catch((err) => { + console.log(err); + }) + } + }, [projectId, selectedVersion?.versionId]) + + return ( + <> + + + + + + + ) +} + +export default ZoneGroup \ No newline at end of file diff --git a/app/src/modules/collaboration/socket/socketResponses.dev.tsx b/app/src/modules/collaboration/socket/socketResponses.dev.tsx index 6028329..3f4bfdb 100644 --- a/app/src/modules/collaboration/socket/socketResponses.dev.tsx +++ b/app/src/modules/collaboration/socket/socketResponses.dev.tsx @@ -1,891 +1,9 @@ -import { useEffect } from "react"; -import * as THREE from "three"; -import gsap from "gsap"; -import { GLTF, GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; -import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; -import { - useSocketStore, - useActiveLayer, - useWallItems, - useLayers, - useUpdateScene, - useWalls, - useDeletedLines, - useNewLines, - useZonePoints, - useZones, -} from "../../../store/builder/store"; +import React from 'react'; -import * as Types from "../../../types/world/worldTypes"; -import * as CONSTANTS from "../../../types/world/worldConstants"; +export default function SocketResponses() { -// import { setFloorItemApi } from "../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; -import objectLineToArray from "../../builder/geomentries/lines/lineConvertions/objectLineToArray"; -import addLineToScene from "../../builder/geomentries/lines/addLineToScene"; -import updateLinesPositions from "../../builder/geomentries/lines/updateLinesPositions"; -import updateLines from "../../builder/geomentries/lines/updateLines"; -import updateDistanceText from "../../builder/geomentries/lines/updateDistanceText"; -import updateFloorLines from "../../builder/geomentries/floors/updateFloorLines"; -import loadWalls from "../../builder/geomentries/walls/loadWalls"; -import RemoveConnectedLines from "../../builder/geomentries/lines/removeConnectedLines"; -import Layer2DVisibility from "../../builder/geomentries/layers/layer2DVisibility"; -import { retrieveGLTF, storeGLTF } from "../../../utils/indexDB/idbUtils"; -import { getZonesApi } from "../../../services/factoryBuilder/zones/getZonesApi"; -import { useParams } from "react-router-dom"; -import { getUserData } from "../../../functions/getUserData"; -import { useSceneContext } from "../../scene/sceneContext"; -import { useVersionContext } from "../../builder/version/versionContext"; - -export default function SocketResponses({ - floorPlanGroup, - lines, - floorGroup, - scene, - onlyFloorlines, - currentLayerPoint, - floorPlanGroupPoint, - floorPlanGroupLine, - dragPointControls, -}: any) { - const { socket } = useSocketStore(); - const { activeLayer, setActiveLayer } = useActiveLayer(); - const { wallItems, setWallItems } = useWallItems(); - const { setLayers } = useLayers(); - const { setUpdateScene } = useUpdateScene(); - const { setWalls } = useWalls(); - const { setDeletedLines } = useDeletedLines(); - const { setNewLines } = useNewLines(); - const { zones, setZones } = useZones(); - const { zonePoints, setZonePoints } = useZonePoints(); - const { selectedVersionStore } = useVersionContext(); - const { selectedVersion } = selectedVersionStore(); - const { projectId } = useParams(); - const { assetStore, eventStore, productStore } = useSceneContext(); - const { addAsset, updateAsset, removeAsset } = assetStore(); - const { organization } = getUserData(); - - useEffect(() => { - - if (!socket) return; - - socket.on("cameraCreateResponse", (data: any) => { - // - }); - - socket.on("userConnectRespones", (data: any) => { - // - }); - - socket.on("userDisConnectRespones", (data: any) => { - // - }); - - socket.on("v1:camera:Response:update", (data: any) => { - // - }); - - socket.on("EnvironmentUpdateResponse", (data: any) => { - // - }); - - socket.on("v1:model-asset:response:add", async (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "Model created successfully") { - try { - - const asset: Asset = { - modelUuid: data.data.modelUuid, - modelName: data.data.modelName, - assetId: data.data.assetId, - 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, - } - - addAsset(asset); - - echo.success("Added model through collaboration"); - } catch (error) { - echo.error("Failed to create model through collaboration"); - } - } else if (data.message === "Model updated successfully") { - try { - - const asset: Asset = { - modelUuid: data.data.modelUuid, - modelName: data.data.modelName, - assetId: data.data.assetId, - 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, - } - - updateAsset(asset.modelUuid, { - position: asset.position, - rotation: asset.rotation, - }); - - echo.success("Updated model through collaboration"); - } catch (error) { - echo.error("Failed to update model through collaboration"); - } - } else { - echo.error("Failed executing action from collaboration"); - } - }); - - socket.on("v1:model-asset:response:delete", (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "Model deleted successfully") { - try { - const deletedUUID = data.data.modelUuid; - - eventStore.getState().removeEvent(deletedUUID); - productStore.getState().deleteEvent(deletedUUID); - - removeAsset(deletedUUID); - - echo.success("Model Removed successfully through collaboration"); - } catch (error) { - echo.error("Failed to remove model through collaboration"); - } - } - }); - - socket.on("v1:Line:response:update", (data: any) => { - // console.log('data: ', data); - 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("v1:Line:response:delete", (data: any) => { - // console.log('data: ', data); - 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("v1: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); - - echo.success("Point Removed!"); - } - }); - - socket.on("v1: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, projectId, selectedVersion?.versionId || ''); - const highestLayer = Math.max( - 1, - lines.current.reduce( - (maxLayer: number, segment: any) => - Math.max(maxLayer, segment.layer || 0), - 0 - ), - zonesData.reduce( - (maxLayer: number, zone: any) => - Math.max(maxLayer, zone.layer || 0), - 0 - ) - ); - - setLayers(highestLayer); - - loadWalls(lines, setWalls); - setUpdateScene(true); - - echo.success("Layer Removed!"); - } - }); - - return () => { - socket.off("cameraCreateResponse"); - socket.off("userConnectRespones"); - socket.off("userDisConnectRespones"); - socket.off("v1:camera:Response:update"); - socket.off("EnvironmentUpdateResponse"); - socket.off("v1:model-asset:response:add"); - socket.off("v1:model-asset:response:delete"); - socket.off("v1:Line:response:update"); - socket.off("v1:Line:response:delete"); - socket.off("v1:Line:response:delete:point"); - socket.off("v1:Line:response:delete:layer"); - } - }, [socket, selectedVersion?.versionId]); - - useEffect(() => { - if (!socket) return; - let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - - socket.on("v1:wallItem:Response:Delete", (data: any) => { - // console.log('data: ', data); - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "wall Item deleted successfully") { - 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); - } - }); - - socket.on("v1:wallItems:Response:Update", (data: any) => { - // console.log('data: ', data); - // - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "wall Item 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); - - // Check THREE.js cache first - const cachedModel = THREE.Cache.get(data.data.assetId); - if (cachedModel) { - handleModelLoad(cachedModel); - return; - } - - // Check IndexedDB cache - retrieveGLTF(data.data.assetId).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.assetId, gltf); - handleModelLoad(gltf); - }); - return; - } - }) - - // Load from backend if not in any cache - loader.load(`${url_Backend_dwinzo}/api/v2/AssetFile/${data.data.assetId}`, async (gltf) => { - try { - const modelBlob = await fetch(`${url_Backend_dwinzo}/api/v2/AssetFile/${data.data.assetId}`).then((res) => res.blob()); - await storeGLTF(data.data.assetId, modelBlob); - THREE.Cache.add(data.data.assetId, gltf); - await handleModelLoad(gltf); - } catch (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, - assetId: data.data.assetId, - 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 === "Updated successfully") { - 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; - }); - } - }); - - return () => { - socket.off("v1:wallItem:Response:Delete"); - socket.off("v1:wallItems:Response:Update"); - }; - }, [socket, 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; - - socket.on("v1: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: { - 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 - }, - vertexShader: ` - varying vec2 vUv; - - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); - } - `, - fragmentShader: ` - varying vec2 vUv; - uniform vec3 uOuterColor; - uniform vec3 uInnerColor; - - void main() { - // Define the size of the white square as a proportion of the face - float borderThickness = 0.2; // Adjust this value for border thickness - if (vUv.x > borderThickness && vUv.x < 1.0 - borderThickness && - vUv.y > borderThickness && vUv.y < 1.0 - borderThickness) { - gl_FragColor = vec4(uInnerColor, 1.0); // White inner square - } else { - gl_FragColor = vec4(uOuterColor, 1.0); // Blue border - } - } - `, - }); - const point = new THREE.Mesh(geometry, material); - point.name = "point"; - point.uuid = line[1]; - point.userData = { type: type, color: pointColour }; - point.position.set(line[0].x, line[0].y, line[0].z); - currentLayerPoint.current.push(point); - - floorPlanGroupPoint.current?.add(point); - }); - if (dragPointControls.current) { - dragPointControls.current!.objects = currentLayerPoint.current; - } - addLineToScene( - new THREE.Vector3(line[0][0].x, line[0][0].y, line[0][0].z), - new THREE.Vector3(line[1][0].x, line[1][0].y, line[1][0].z), - lineColour, - line, - floorPlanGroupLine - ); - lines.current.push(line); - - const zonesData = await getZonesApi(organization, projectId, selectedVersion?.versionId || ''); - const highestLayer = Math.max( - 1, - lines.current.reduce( - (maxLayer: number, segment: any) => - Math.max(maxLayer, segment.layer || 0), - 0 - ), - zonesData.reduce( - (maxLayer: number, zone: any) => - Math.max(maxLayer, zone.layer || 0), - 0 - ) - ); - - setLayers(highestLayer); - - Layer2DVisibility( - activeLayer, - floorPlanGroup, - floorPlanGroupLine, - floorPlanGroupPoint, - currentLayerPoint, - dragPointControls - ); - - loadWalls(lines, setWalls); - setUpdateScene(true); - } - }); - - return () => { - socket.off("v1:Line:response:create"); - }; - }, [socket, activeLayer, selectedVersion?.versionId]); - - useEffect(() => { - if (!socket) return; - - socket.on("v1:zone:response:updates", (data: any) => { - // console.log('data: ', data); - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - - if (data.message === "zone created") { - const pointsArray: [number, number, number][] = data.data.points; - const vector3Array = pointsArray.map( - ([x, y, z]) => new THREE.Vector3(x, y, z) - ); - const newZones = [...zones, data.data]; - setZones(newZones); - const updatedZonePoints = [...zonePoints, ...vector3Array]; - setZonePoints(updatedZonePoints); - - const highestLayer = Math.max( - 1, - lines.current.reduce( - (maxLayer: number, segment: any) => - Math.max(maxLayer, segment.layer || 0), - 0 - ), - newZones.reduce( - (maxLayer: number, zone: any) => - Math.max(maxLayer, zone.layer || 0), - 0 - ) - ); - - setLayers(highestLayer); - setUpdateScene(true); - } - - if (data.message === "zone updated") { - const updatedZones = zones.map((zone: any) => - zone.zoneUuid === data.data.zoneUuid ? data.data : zone - ); - setZones(updatedZones); - setUpdateScene(true); - } - }); - - socket.on("v1:zone:response:delete", (data: any) => { - // console.log('data: ', data); - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "zone deleted") { - const updatedZones = zones.filter( - (zone: any) => zone.zoneUuid !== data.data.zoneUuid - ); - setZones(updatedZones); - - const zoneIndex = zones.findIndex( - (zone: any) => zone.zoneUuid === data.data.zoneUuid - ); - 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("v1:zone:response:updates"); - socket.off("v1:zone:response:delete"); - }; - }, [socket, zones, zonePoints]); - - return <>; + return ( + <> + + ); } diff --git a/app/src/modules/market/MarketPlace.tsx b/app/src/modules/market/MarketPlace.tsx index 1bf446e..85720d2 100644 --- a/app/src/modules/market/MarketPlace.tsx +++ b/app/src/modules/market/MarketPlace.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import FilterSearch from "./FilterSearch"; import CardsContainer from "./CardsContainer"; -import { getAssetImages } from "../../services/factoryBuilder/assest/assets/getAssetImages"; +import { getAssetImages } from "../../services/factoryBuilder/asset/assets/getAssetImages"; import SkeletonUI from "../../components/templates/SkeletonUI"; interface ModelData { CreatedBy: string; diff --git a/app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx b/app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx index 05b43c6..b30b03e 100644 --- a/app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx +++ b/app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx @@ -51,7 +51,7 @@ const BoundingBox = ({ boundingBoxRef, isPerAsset = true }: BoundingBoxProps) => return { points: getBoxLines(min, max), - position: [position.x, center.y, position.z], + position: [position.x, position.y, position.z], rotation: rotation.toArray(), size: size.toArray(), }; @@ -93,7 +93,7 @@ const BoundingBox = ({ boundingBoxRef, isPerAsset = true }: BoundingBoxProps) => color={savedTheme === "dark" ? "#c4abf1" : "#6f42c1"} lineWidth={2.7} segments - position={[box.position[0], 0, box.position[2]]} + position={[box.position[0], box.position[1], box.position[2]]} quaternion={new THREE.Quaternion(...box.rotation)} /> ; - object: number; + boundingBoxRef: React.RefObject; + object: number; } +const material = new LineBasicMaterial({ color: "#d2baff" }); + +const DIRECTION_LABEL_MAP = { + textPosX: "textPosX", + textNegX: "textNegX", + textPosZ: "textPosZ", + textNegZ: "textNegZ", +} as const; + const DistanceFindingControls = ({ - boundingBoxRef, - object, + boundingBoxRef, + object, }: DistanceFindingControlsProps) => { - const { camera, scene } = useThree(); - const [labelValues, setLabelValues] = useState<{ - textPosX: any; - textNegX: any; - textPosZ: any; - textNegZ: any; - }>({ - textPosX: "", - textNegX: "", - textPosZ: "", - textNegZ: "", - }); + const { camera, scene } = useThree(); + const [labelValues, setLabelValues] = useState>({ + textPosX: "", + textNegX: "", + textPosZ: "", + textNegZ: "", + }); - // Refs for measurement lines - const line1 = useRef(null); - const line2 = useRef(null); - const line3 = useRef(null); - const line4 = useRef(null); - const line5 = useRef(null); - - // Refs for measurement text labels - const textPosX = useRef(null); - const textNegX = useRef(null); - const textPosZ = useRef(null); - const textNegZ = useRef(null); - const textPosY = useRef(null); - - // Store line geometries to avoid recreation - const lineGeometries = useRef({ - posX: new BufferGeometry(), - negX: new BufferGeometry(), - posZ: new BufferGeometry(), - negZ: new BufferGeometry(), - posY: new BufferGeometry(), - }); - - useFrame(() => { - if (!boundingBoxRef?.current) return; - - boundingBoxRef.current.geometry.computeBoundingBox(); - const bbox = boundingBoxRef.current.geometry.boundingBox; - - if (!bbox) return; - - const size = { - x: bbox.max.x - bbox.min.x, - y: bbox.max.y - bbox.min.y, - z: bbox.max.z - bbox.min.z, + const lineRefs = { + posX: useRef(null), + negX: useRef(null), + posZ: useRef(null), + negZ: useRef(null), + posY: useRef(null), }; - const vec = boundingBoxRef.current?.getWorldPosition(new Vector3()).clone(); + const textRefs = { + textPosX: useRef(null), + textNegX: useRef(null), + textPosZ: useRef(null), + textNegZ: useRef(null), + textPosY: useRef(null), + }; - if (!vec) return; - updateLine({ - line: line1.current, - geometry: lineGeometries.current.posX, - direction: new Vector3(1, 0, 0), // Positive X - angle: "pos", - mesh: textPosX, - vec, - size, + const lineGeometries = useRef({ + posX: new BufferGeometry(), + negX: new BufferGeometry(), + posZ: new BufferGeometry(), + negZ: new BufferGeometry(), + posY: new BufferGeometry(), }); - updateLine({ - line: line2.current, - geometry: lineGeometries.current.negX, - direction: new Vector3(-1, 0, 0), // Negative X - angle: "neg", - mesh: textNegX, - vec, - size, + + useFrame(() => { + const bboxMesh = boundingBoxRef?.current; + if (!bboxMesh) return; + + bboxMesh.geometry.computeBoundingBox(); + const bbox = bboxMesh.geometry.boundingBox; + if (!bbox) return; + + const size = { + x: bbox.max.x - bbox.min.x, + y: bbox.max.y - bbox.min.y, + z: bbox.max.z - bbox.min.z, + }; + + const center = bboxMesh.getWorldPosition(new Vector3()).clone(); + if (!center) return; + + updateLine("posX", new Vector3(1, 0, 0), "pos", size, center); + updateLine("negX", new Vector3(-1, 0, 0), "neg", size, center); + updateLine("posZ", new Vector3(0, 0, 1), "pos", size, center); + updateLine("negZ", new Vector3(0, 0, -1), "neg", size, center); + updateLine("posY", new Vector3(0, -1, 0), "posY", size, center); }); - updateLine({ - line: line3.current, - geometry: lineGeometries.current.posZ, - direction: new Vector3(0, 0, 1), // Positive Z - angle: "pos", - mesh: textPosZ, - vec, - size, - }); - updateLine({ - line: line4.current, - geometry: lineGeometries.current.negZ, - direction: new Vector3(0, 0, -1), // Negative Z - angle: "neg", - mesh: textNegZ, - vec, - size, - }); - updateLine({ - line: line5.current, - geometry: lineGeometries.current.posY, - direction: new Vector3(0, -1, 0), // Down (Y) - angle: "posY", - mesh: textPosY, - vec, - size, - }); - }); - const updateLine = ({ - line, - geometry, - direction, - angle, - mesh, - vec, - size, - }: { - line: Line | null; - geometry: BufferGeometry; - direction: Vector3; - angle: string; - mesh: React.RefObject; - vec: Vector3; - size: { x: number; y: number; z: number }; - }) => { - if (!line) return; + const updateLine = ( + key: keyof typeof lineRefs, + direction: Vector3, + angle: string, + size: { x: number; y: number; z: number }, + origin: Vector3 + ) => { + const line = lineRefs[key].current; + const geometry = lineGeometries.current[key]; + const mesh = textRefs[`text${key[0].toUpperCase() + key.slice(1)}` as keyof typeof textRefs]; - const points = []; + if (!line) return; - if (angle === "pos") { - points[0] = new Vector3(vec.x, vec.y, vec.z).add( - new Vector3((direction.x * size.x) / 2, 0, (direction.z * size.z) / 2) - ); - } else if (angle === "neg") { - points[0] = new Vector3(vec.x, vec.y, vec.z).sub( - new Vector3((-direction.x * size.x) / 2, 0, (-direction.z * size.z) / 2) - ); - } else if (angle === "posY") { - points[0] = new Vector3(vec.x, vec.y, vec.z).sub( - new Vector3(0, size.y / 2, 0) - ); - } + const points: Vector3[] = []; + const halfSize = new Vector3(size.x / 2, size.y / 2, size.z / 2); - const ray = new Raycaster(); - if (camera) ray.camera = camera; - ray.set(new Vector3(vec.x, vec.y, vec.z), direction); - ray.params.Line.threshold = 0.1; + if (angle === "pos") { + points[0] = origin.clone().add(direction.clone().multiply(halfSize)); + } else if (angle === "neg") { + points[0] = origin.clone().sub(direction.clone().multiply(halfSize)); + } else if (angle === "posY") { + points[0] = origin.clone().sub(new Vector3(0, size.y / 2, 0)); + } - // Find intersection points - const wallsGroup = scene.children.find((val) => - val?.name.includes("Walls") + const ray = new Raycaster(); + ray.camera = camera; + ray.set(origin, direction); + ray.params.Line.threshold = 0.1; + + const wallsGroup = scene.children.find((val) => + val?.name.includes("Walls") + ); + const intersects = wallsGroup + ? ray.intersectObjects([wallsGroup], true) + : []; + + const intersect = intersects.find((i) => + i.object.name.includes("Wall") + ); + + if (intersect) { + points[1] = angle !== "posY" ? intersect.point : new Vector3(origin.x, 0, origin.z); + } + + if (points[1]) { + geometry.dispose(); + geometry.setFromPoints(points); + line.geometry = geometry; + + const distance = points[0].distanceTo(points[1]).toFixed(2); + + if (mesh?.current) { + geometry.computeBoundingSphere(); + mesh.current.position.copy(geometry.boundingSphere!.center); + + const labelEl = document.getElementById(mesh.current.name); + if (labelEl) { + labelEl.innerText = `${distance}m`; + + if (DIRECTION_LABEL_MAP[labelEl.id as keyof typeof DIRECTION_LABEL_MAP]) { + setLabelValues((prev) => ({ + ...prev, + [labelEl.id]: distance, + })); + } + } + } + } else { + geometry.dispose(); + geometry.setFromPoints([new Vector3(), new Vector3()]); + line.geometry = geometry; + + const labelEl = document.getElementById(mesh?.current?.name ?? ""); + if (labelEl && DIRECTION_LABEL_MAP[labelEl.id as keyof typeof DIRECTION_LABEL_MAP]) { + labelEl.innerText = ""; + setLabelValues((prev) => ({ + ...prev, + [labelEl.id]: "", + })); + } + } + }; + + const renderLabel = (id: keyof typeof textRefs) => ( + + +
{labelValues[id]}
+ +
); - const intersects = wallsGroup - ? ray.intersectObjects([wallsGroup], true) - : []; - // Find intersection point - if (intersects[0]) { - for (const intersect of intersects) { - if (intersect.object.name.includes("Wall")) { - points[1] = - angle !== "posY" ? intersect.point : new Vector3(vec.x, 0, vec.z); // Floor - break; - } - } - } - if (points[1]) { - geometry.dispose(); - geometry.setFromPoints([points[0], points[1]]); - line.geometry = geometry; - - // Calculate the distance only once - const distance = points[0].distanceTo(points[1]).toFixed(2); - - // Update measurement text - if (mesh?.current) { - geometry.computeBoundingSphere(); - const center = geometry.boundingSphere?.center; - if (center) { - mesh.current.position.copy(center); - } - - const label = document.getElementById(mesh.current.name); - if (label) { - label.innerText = `${distance}m`; - - // Update specific label state based on the label ID - switch (label.id) { - case "textPosX": - setLabelValues((prevState) => ({ ...prevState, textPosX: distance })); - break; - case "textNegX": - setLabelValues((prevState) => ({ ...prevState, textNegX: distance })); - break; - case "textPosZ": - setLabelValues((prevState) => ({ ...prevState, textPosZ: distance })); - break; - case "textNegZ": - setLabelValues((prevState) => ({ ...prevState, textNegZ: distance })); - break; - default: - break; - } - } - } - } else { - // No intersection found - clear the line - geometry.dispose(); - geometry.setFromPoints([new Vector3(), new Vector3()]); - line.geometry = geometry; - - const label = document.getElementById(mesh?.current?.name ?? ""); - if (label) { - label.innerText = ""; - - // Clear the corresponding label value in the state - switch (label.id) { - case "textPosX": - setLabelValues((prevState) => ({ ...prevState, textPosX: "" })); - break; - case "textNegX": - setLabelValues((prevState) => ({ ...prevState, textNegX: "" })); - break; - case "textPosZ": - setLabelValues((prevState) => ({ ...prevState, textPosZ: "" })); - break; - case "textNegZ": - setLabelValues((prevState) => ({ ...prevState, textNegZ: "" })); - break; - default: - break; - } - } - } - - }; - - const Material = new LineBasicMaterial({ color: "#d2baff" }); - - return ( - <> - {/* Measurement text labels */} - {boundingBoxRef.current && object > 0 && ( + return ( <> - - -
{labelValues.textPosX}
- -
+ {boundingBoxRef.current && object > 0 && ( + + {renderLabel("textPosX")} + {renderLabel("textNegX")} + {renderLabel("textPosZ")} + {renderLabel("textNegZ")} - - -
{labelValues.textNegX}
- -
- - - -
{labelValues.textPosZ}
- -
- - - -
{labelValues.textNegZ}
- -
- - {/* Measurement lines */} - - - - + + + + +
+ )} - ) - } - - ); + ); }; export default DistanceFindingControls; diff --git a/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx b/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx index ef56601..5194952 100644 --- a/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx @@ -2,7 +2,7 @@ import * as THREE from "three"; import { useEffect, useMemo } from "react"; import { useFrame, useThree } from "@react-three/fiber"; import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store"; -// import { setAssetsApi } from '../../../../services/factoryBuilder/assest/floorAsset/setAssetsApi'; +// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi'; import * as Types from "../../../../types/world/worldTypes"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; import { useParams } from "react-router-dom"; diff --git a/app/src/modules/scene/controls/selectionControls/moveControls.tsx b/app/src/modules/scene/controls/selectionControls/moveControls.tsx index 70d32e9..79cf195 100644 --- a/app/src/modules/scene/controls/selectionControls/moveControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/moveControls.tsx @@ -2,7 +2,7 @@ import * as THREE from "three"; import { useEffect, useMemo, useRef, useState } from "react"; import { useFrame, useThree } from "@react-three/fiber"; import { useSelectedAssets, useSocketStore, useToggleView, } from "../../../../store/builder/store"; -// import { setAssetsApi } from '../../../../services/factoryBuilder/assest/floorAsset/setAssetsApi'; +// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi'; import * as Types from "../../../../types/world/worldTypes"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi"; @@ -165,10 +165,21 @@ function MoveControls({ if (movedObjects.length > 0) { const intersectionPoint = new THREE.Vector3(); raycaster.setFromCamera(pointer, camera); - const point = raycaster.ray.intersectPlane(plane, intersectionPoint); + let point = raycaster.ray.intersectPlane(plane, intersectionPoint); + const floorsGroup = scene.getObjectByName("Floors-Group") as Types.Group | null; + const floorChildren = floorsGroup?.children ?? []; + const floorIntersections = raycaster.intersectObjects([...floorChildren], true); + const intersectedFloor = floorIntersections.find((intersect) => intersect.object.name.includes("Floor")); + + if (intersectedFloor && selectedAssets.length === 1) { + if (intersectedFloor.object.userData.floorUuid) { + point = new THREE.Vector3(intersectedFloor.point.x, intersectedFloor.object.userData.floorDepth, intersectedFloor.point.z); + } + } if (point) { let targetX = point.x; + let targetY = point.y; let targetZ = point.z; if (keyEvent === "Ctrl") { @@ -192,7 +203,7 @@ function MoveControls({ selectionGroup.current.position.lerp( new THREE.Vector3( targetX - (position.x - selectionGroup.current.position.x), - selectionGroup.current.position.y, + targetY - (position.y - selectionGroup.current.position.y), targetZ - (position.z - selectionGroup.current.position.z) ), moveSpeed @@ -230,9 +241,13 @@ function MoveControls({ movedObjects.forEach(async (obj: THREE.Object3D) => { if (obj && AssetGroup.current) { - const worldPosition = new THREE.Vector3(); + let worldPosition = new THREE.Vector3(); obj.getWorldPosition(worldPosition); + if (worldPosition.y < 0) { + worldPosition.y = 0; + } + selectionGroup.current.remove(obj); obj.position.copy(worldPosition); diff --git a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx index 6592e72..216d4f7 100644 --- a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx @@ -2,7 +2,7 @@ import * as THREE from "three"; import { useEffect, useMemo, useRef } from "react"; import { useFrame, useThree } from "@react-three/fiber"; import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store"; -// import { setAssetsApi } from '../../../../services/factoryBuilder/assest/floorAsset/setAssetsApi'; +// import { setAssetsApi } from '../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi'; import * as Types from "../../../../types/world/worldTypes"; import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi"; import { useParams } from "react-router-dom"; diff --git a/app/src/modules/scene/controls/selectionControls/selectionControls.tsx b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx index 1f26a6b..645ed1c 100644 --- a/app/src/modules/scene/controls/selectionControls/selectionControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx @@ -5,7 +5,7 @@ import { SelectionHelper } from "./selectionHelper"; import { useFrame, useThree } from "@react-three/fiber"; import { useSelectedAssets, useSocketStore, useToggleView, useToolMode, } from "../../../../store/builder/store"; import BoundingBox from "./boundingBoxHelper"; -// import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; +// import { deleteFloorItem } from '../../../../services/factoryBuilder/asset/floorAsset/deleteFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; import DuplicationControls from "./duplicationControls"; @@ -225,7 +225,7 @@ const SelectionControls: React.FC = () => { selectedObjects.forEach((object) => { let currentObject: THREE.Object3D | null = object; while (currentObject) { - if (currentObject.userData.modelUuid) { + if (currentObject.userData.modelUuid && !currentObject.userData.wallAssetType) { Objects.add(currentObject); break; } diff --git a/app/src/modules/scene/controls/transformControls/transformControls.tsx b/app/src/modules/scene/controls/transformControls/transformControls.tsx index 0b6a061..e0ce1cf 100644 --- a/app/src/modules/scene/controls/transformControls/transformControls.tsx +++ b/app/src/modules/scene/controls/transformControls/transformControls.tsx @@ -6,7 +6,7 @@ import { useThree } from "@react-three/fiber"; import { useEffect, useState } from "react"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi"; -// import { setAssetsApi } from "../../../../services/factoryBuilder/assest/floorAsset/setAssetsApi"; +// import { setAssetsApi } from "../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi"; import { useParams } from "react-router-dom"; import { useProductContext } from "../../../simulation/products/productContext"; import { getUserData } from "../../../../functions/getUserData"; diff --git a/app/src/modules/scene/environment/ground.tsx b/app/src/modules/scene/environment/ground.tsx index ef51791..f6baeef 100644 --- a/app/src/modules/scene/environment/ground.tsx +++ b/app/src/modules/scene/environment/ground.tsx @@ -1,14 +1,13 @@ import { useTileDistance, useToggleView } from "../../../store/builder/store"; import * as CONSTANTS from "../../../types/world/worldConstants"; -const Ground = ({ grid, plane }: any) => { +const Ground = ({ plane }: any) => { const { toggleView } = useToggleView(); const { planeValue, gridValue } = useTileDistance(); return ( diff --git a/app/src/modules/scene/postProcessing/postProcessing.tsx b/app/src/modules/scene/postProcessing/postProcessing.tsx index 25d0e0c..6c2f8d6 100644 --- a/app/src/modules/scene/postProcessing/postProcessing.tsx +++ b/app/src/modules/scene/postProcessing/postProcessing.tsx @@ -15,7 +15,7 @@ export default function PostProcessing() { const { selectedWallItem } = useSelectedWallItem(); const { selectedFloorItem } = useSelectedFloorItem(); const { selectedEventSphere } = useSelectedEventSphere(); - const { selectedAisle, selectedWall, selectedDecal } = useBuilderStore(); + const { selectedAisle, selectedWall, selectedDecal, selectedFloor, selectedWallAsset, deletableWallAsset } = useBuilderStore(); function flattenChildren(children: any[]) { const allChildren: any[] = []; @@ -44,6 +44,18 @@ export default function PostProcessing() { // console.log('selectedWall: ', selectedWall); }, [selectedWall]) + useEffect(() => { + // console.log('selectedFloor: ', selectedFloor); + }, [selectedFloor]) + + useEffect(() => { + // console.log('selectedWallAsset: ', selectedWallAsset); + }, [selectedWallAsset]) + + useEffect(() => { + // console.log('deletableWallAsset: ', deletableWallAsset); + }, [deletableWallAsset]) + return ( + {selectedWallAsset && ( + + )} + {deletableWallAsset && ( + + )} {selectedAisle && ( )} + {selectedFloor && ( + + )} {selectedDecal && ( - - + + diff --git a/app/src/modules/simulation/vehicle/navMesh/navMeshDetails.tsx b/app/src/modules/simulation/vehicle/navMesh/navMeshDetails.tsx index df660fe..106f652 100644 --- a/app/src/modules/simulation/vehicle/navMesh/navMeshDetails.tsx +++ b/app/src/modules/simulation/vehicle/navMesh/navMeshDetails.tsx @@ -9,11 +9,9 @@ import * as Types from "../../../../types/world/worldTypes"; interface NavMeshDetailsProps { setNavMesh: (navMesh: any) => void; groupRef: React.MutableRefObject; - lines: Types.RefLines; } export default function NavMeshDetails({ - lines, setNavMesh, groupRef, }: NavMeshDetailsProps) { @@ -63,7 +61,7 @@ export default function NavMeshDetails({ }; initializeNavigation(); - }, [scene, groupRef, lines.current]); + }, [scene, groupRef]); return null; } diff --git a/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx b/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx index eaf5a24..34d9f26 100644 --- a/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx +++ b/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx @@ -2,26 +2,23 @@ import * as THREE from "three"; import { useEffect } from "react"; import * as turf from "@turf/turf"; import * as Types from "../../../../types/world/worldTypes"; -import arrayLinesToObject from "../../../builder/geomentries/lines/lineConvertions/arrayLinesToObject"; import { useThree } from "@react-three/fiber"; import { useSceneContext } from "../../../scene/sceneContext"; interface PolygonGeneratorProps { groupRef: React.MutableRefObject; - lines: Types.RefLines; } export default function PolygonGenerator({ groupRef, - lines, }: PolygonGeneratorProps) { const { aisleStore } = useSceneContext(); const { aisles } = aisleStore(); const { scene } = useThree(); useEffect(() => { - let allLines = arrayLinesToObject(lines.current); - const wallLines = allLines?.filter((line) => line?.type === "WallLine"); + // let allLines = arrayLinesToObject(lines.current); + // const wallLines = allLines?.filter((line) => line?.type === "WallLine"); const result = aisles .filter( (aisle) => @@ -64,9 +61,9 @@ export default function PolygonGenerator({ }); }); - const wallPoints = wallLines - .map((pair) => pair?.line.map((vals) => vals.position)) - .filter((wall): wall is THREE.Vector3[] => !!wall); + // const wallPoints = wallLines + // .map((pair) => pair?.line.map((vals) => vals.position)) + // .filter((wall): wall is THREE.Vector3[] => !!wall); if (!result || result.some((line) => !line)) { @@ -84,7 +81,7 @@ export default function PolygonGenerator({ const polygons = turf.polygonize(turf.featureCollection(validLineFeatures)); - renderWallGeometry(wallPoints); + // renderWallGeometry(wallPoints); if (polygons.features.length > 0) { polygons.features.forEach((feature) => { @@ -119,7 +116,7 @@ export default function PolygonGenerator({ }); } - }, [lines.current, aisles, scene]); + }, [ aisles, scene]); const renderWallGeometry = (walls: THREE.Vector3[][]) => { walls.forEach((wall) => { diff --git a/app/src/services/factoryBuilder/aisle/deleteAisleApi.ts b/app/src/services/factoryBuilder/aisle/deleteAisleApi.ts index 5ef96a3..28fde46 100644 --- a/app/src/services/factoryBuilder/aisle/deleteAisleApi.ts +++ b/app/src/services/factoryBuilder/aisle/deleteAisleApi.ts @@ -10,7 +10,7 @@ export const deleteAisleApi = async (aisleUuid: string, projectId: string, versi token: localStorage.getItem("token") || "", // Coerce null to empty string refresh_token: localStorage.getItem("refreshToken") || "", }, - body: JSON.stringify({ aisleUuid, projectId }), + body: JSON.stringify({ aisleUuid, projectId, versionId }), }); const newAccessToken = response.headers.get("x-access-token"); if (newAccessToken) { diff --git a/app/src/services/factoryBuilder/aisle/createAisleApi.ts b/app/src/services/factoryBuilder/aisle/upsertAisleApi.ts similarity index 97% rename from app/src/services/factoryBuilder/aisle/createAisleApi.ts rename to app/src/services/factoryBuilder/aisle/upsertAisleApi.ts index 27ae378..b980c61 100644 --- a/app/src/services/factoryBuilder/aisle/createAisleApi.ts +++ b/app/src/services/factoryBuilder/aisle/upsertAisleApi.ts @@ -1,6 +1,6 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -export const createAisleApi = async ( +export const upsertAisleApi = async ( aisleUuid: string, points: any, type: Object, diff --git a/app/src/services/factoryBuilder/assest/wallAsset/deleteWallItemApi.ts b/app/src/services/factoryBuilder/assest/wallAsset/deleteWallItemApi.ts deleted file mode 100644 index 84dee42..0000000 --- a/app/src/services/factoryBuilder/assest/wallAsset/deleteWallItemApi.ts +++ /dev/null @@ -1,42 +0,0 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; - -export const deleteWallItem = async ( - organization: string, - modelUuid: string, - modelName: string -) => { - try { - const response = await fetch( - `${url_Backend_dwinzo}/api/v1/deleteWallItem`, - { - method: "DELETE", - headers: { - Authorization: "Bearer ", - "Content-Type": "application/json", - token: localStorage.getItem("token") || "", - refresh_token: localStorage.getItem("refreshToken") || "", - }, - body: JSON.stringify({ organization, modelUuid, modelName }), - } - ); - const newAccessToken = response.headers.get("x-access-token"); - if (newAccessToken) { - //console.log("New token received:", newAccessToken); - localStorage.setItem("token", newAccessToken); - } - - if (!response.ok) { - console.error("Failed to delete Wall Item"); - } - - const result = await response.json(); - return result; - } catch (error) { - echo.error("Failed to delete wall items"); - if (error instanceof Error) { - console.log(error.message); - } else { - console.log("An unknown error occurred"); - } - } -}; diff --git a/app/src/services/factoryBuilder/assest/wallAsset/getWallItemsApi.ts b/app/src/services/factoryBuilder/assest/wallAsset/getWallItemsApi.ts deleted file mode 100644 index 55a09f0..0000000 --- a/app/src/services/factoryBuilder/assest/wallAsset/getWallItemsApi.ts +++ /dev/null @@ -1,37 +0,0 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; - -export const getWallItems = async (organization: string, projectId?: string, versionId?: string) => { - try { - const response = await fetch( - `${url_Backend_dwinzo}/api/V1/walls/${projectId}/${versionId}`, - { - method: "GET", - headers: { - Authorization: "Bearer ", - "Content-Type": "application/json", - token: localStorage.getItem("token") || "", - refresh_token: localStorage.getItem("refreshToken") || "", - }, - } - ); - const newAccessToken = response.headers.get("x-access-token"); - if (newAccessToken) { - //console.log("New token received:", newAccessToken); - localStorage.setItem("token", newAccessToken); - } - // console.log('response: ', response); - if (!response.ok) { - console.error("Failed to get Wall Items"); - } - - const result = await response.json(); - return result; - } catch (error) { - echo.error("Failed to get wall items"); - if (error instanceof Error) { - console.log(error.message); - } else { - console.log("An unknown error occurred"); - } - } -}; diff --git a/app/src/services/factoryBuilder/assest/wallAsset/setWallItemApi.ts b/app/src/services/factoryBuilder/assest/wallAsset/setWallItemApi.ts deleted file mode 100644 index b7a0dbd..0000000 --- a/app/src/services/factoryBuilder/assest/wallAsset/setWallItemApi.ts +++ /dev/null @@ -1,55 +0,0 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; - -export const setWallItem = async ( - organization: string, - modelUuid: string, - modelName: string, - type: string, - csgposition: Object, - csgscale: Object, - position: Object, - quaternion: Object, - scale: Object -) => { - try { - const response = await fetch(`${url_Backend_dwinzo}/api/v1/setWallItems`, { - method: "POST", - headers: { - Authorization: "Bearer ", - "Content-Type": "application/json", - token: localStorage.getItem("token") || "", - refresh_token: localStorage.getItem("refreshToken") || "", - }, - body: JSON.stringify({ - organization, - modelUuid, - modelName, - position, - type, - csgposition, - csgscale, - quaternion, - scale, - }), - }); - - const newAccessToken = response.headers.get("x-access-token"); - if (newAccessToken) { - //console.log("New token received:", newAccessToken); - localStorage.setItem("token", newAccessToken); - } - if (!response.ok) { - console.error("Failed to set or update Wall Item"); - } - - const result = await response.json(); - return result; - } catch (error) { - echo.error("Failed to set wall items"); - if (error instanceof Error) { - console.log(error.message); - } else { - console.log("An unknown error occurred"); - } - } -}; diff --git a/app/src/services/factoryBuilder/assest/assets/getAssetImages.ts b/app/src/services/factoryBuilder/asset/assets/getAssetImages.ts similarity index 96% rename from app/src/services/factoryBuilder/assest/assets/getAssetImages.ts rename to app/src/services/factoryBuilder/asset/assets/getAssetImages.ts index faba588..7bc7fca 100644 --- a/app/src/services/factoryBuilder/assest/assets/getAssetImages.ts +++ b/app/src/services/factoryBuilder/asset/assets/getAssetImages.ts @@ -1,24 +1,24 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - -export const getAssetImages = async (cursor?: string) => { - try { - const response = await fetch( - `${url_Backend_dwinzo}/api/v3/AssetDatas?limit=10${cursor ? `&cursor=${cursor}` : ""}`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - } - ); - - if (!response.ok) { - console.error("Failed to fetch assets"); - } - - return await response.json(); - } catch (error: any) { - echo.error("Failed to get asset image"); - console.log(error.message); - } -}; +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; + +export const getAssetImages = async (cursor?: string) => { + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v3/AssetDatas?limit=10${cursor ? `&cursor=${cursor}` : ""}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + } + ); + + if (!response.ok) { + console.error("Failed to fetch assets"); + } + + return await response.json(); + } catch (error: any) { + echo.error("Failed to get asset image"); + console.log(error.message); + } +}; diff --git a/app/src/services/factoryBuilder/assest/assets/getAssetModel.ts b/app/src/services/factoryBuilder/asset/assets/getAssetModel.ts similarity index 96% rename from app/src/services/factoryBuilder/assest/assets/getAssetModel.ts rename to app/src/services/factoryBuilder/asset/assets/getAssetModel.ts index 91c33b3..27fe88a 100644 --- a/app/src/services/factoryBuilder/assest/assets/getAssetModel.ts +++ b/app/src/services/factoryBuilder/asset/assets/getAssetModel.ts @@ -1,29 +1,29 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - -export const getAssetModel = async (modelId: string) => { - try { - const response = await fetch( - `${url_Backend_dwinzo}/api/v2/AssetFile/${modelId}`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - } - ); - - if (!response.ok) { - console.error("Failed to fetch model"); - } - - const result = await response.json(); - return result; - } catch (error) { - echo.error("Failed to get asset model"); - if (error instanceof Error) { - console.log(error.message); - } else { - console.log("An unknown error occurred"); - } - } -}; +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; + +export const getAssetModel = async (modelId: string) => { + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v2/AssetFile/${modelId}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + } + ); + + if (!response.ok) { + console.error("Failed to fetch model"); + } + + const result = await response.json(); + return result; + } catch (error) { + echo.error("Failed to get asset model"); + if (error instanceof Error) { + console.log(error.message); + } else { + console.log("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/factoryBuilder/assest/assets/getCategoryAsset.ts b/app/src/services/factoryBuilder/asset/assets/getCategoryAsset.ts similarity index 100% rename from app/src/services/factoryBuilder/assest/assets/getCategoryAsset.ts rename to app/src/services/factoryBuilder/asset/assets/getCategoryAsset.ts diff --git a/app/src/services/factoryBuilder/assest/floorAsset/deleteFloorItemApi.ts b/app/src/services/factoryBuilder/asset/floorAsset/deleteFloorItemApi.ts similarity index 96% rename from app/src/services/factoryBuilder/assest/floorAsset/deleteFloorItemApi.ts rename to app/src/services/factoryBuilder/asset/floorAsset/deleteFloorItemApi.ts index a509909..1e66ec7 100644 --- a/app/src/services/factoryBuilder/assest/floorAsset/deleteFloorItemApi.ts +++ b/app/src/services/factoryBuilder/asset/floorAsset/deleteFloorItemApi.ts @@ -1,42 +1,42 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; - -export const deleteFloorItem = async ( - organization: string, - modelUuid: string, - modelName: string -) => { - try { - const response = await fetch( - `${url_Backend_dwinzo}/api/v1/deletefloorItem`, - { - method: "DELETE", - headers: { - Authorization: "Bearer ", - "Content-Type": "application/json", - token: localStorage.getItem("token") || "", - refresh_token: localStorage.getItem("refreshToken") || "", - }, - body: JSON.stringify({ organization, modelUuid, modelName }), - } - ); - const newAccessToken = response.headers.get("x-access-token"); - if (newAccessToken) { - //console.log("New token received:", newAccessToken); - localStorage.setItem("token", newAccessToken); - } - - if (!response.ok) { - console.error("Failed to delete Floor Item"); - } - - const result = await response.json(); - return result; - } catch (error) { - echo.error("Failed to delete floor item"); - if (error instanceof Error) { - console.log(error.message); - } else { - console.log("An unknown error occurred"); - } - } -}; +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const deleteFloorItem = async ( + organization: string, + modelUuid: string, + modelName: string +) => { + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/v1/deletefloorItem`, + { + method: "DELETE", + headers: { + Authorization: "Bearer ", + "Content-Type": "application/json", + token: localStorage.getItem("token") || "", + refresh_token: localStorage.getItem("refreshToken") || "", + }, + body: JSON.stringify({ organization, modelUuid, modelName }), + } + ); + const newAccessToken = response.headers.get("x-access-token"); + if (newAccessToken) { + //console.log("New token received:", newAccessToken); + localStorage.setItem("token", newAccessToken); + } + + if (!response.ok) { + console.error("Failed to delete Floor Item"); + } + + const result = await response.json(); + return result; + } catch (error) { + echo.error("Failed to delete floor item"); + if (error instanceof Error) { + console.log(error.message); + } else { + console.log("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/factoryBuilder/assest/floorAsset/getFloorItemsApi.ts b/app/src/services/factoryBuilder/asset/floorAsset/getFloorItemsApi.ts similarity index 96% rename from app/src/services/factoryBuilder/assest/floorAsset/getFloorItemsApi.ts rename to app/src/services/factoryBuilder/asset/floorAsset/getFloorItemsApi.ts index 83038fc..5ead053 100644 --- a/app/src/services/factoryBuilder/assest/floorAsset/getFloorItemsApi.ts +++ b/app/src/services/factoryBuilder/asset/floorAsset/getFloorItemsApi.ts @@ -1,39 +1,39 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; - -export const getFloorAssets = async (organization: string, projectId?: string, versionId?: string) => { - try { - const response = await fetch( - `${url_Backend_dwinzo}/api/V1/floorAssets/${projectId}/${versionId}`, - { - method: "GET", - headers: { - Authorization: "Bearer ", - "Content-Type": "application/json", - token: localStorage.getItem("token") || "", - refresh_token: localStorage.getItem("refreshToken") || "", - }, - } - ); - const newAccessToken = response.headers.get("x-access-token"); - if (newAccessToken) { - //console.log("New token received:", newAccessToken); - localStorage.setItem("token", newAccessToken); - } - - // console.log('response: ', response); - if (!response.ok) { - console.error("Failed to get assets"); - } - - const result = await response.json(); - - return result; - } catch (error) { - echo.error("Failed to get floor asset"); - if (error instanceof Error) { - console.log(error.message); - } else { - console.log("An unknown error occurred"); - } - } -}; +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const getFloorAssets = async (organization: string, projectId?: string, versionId?: string) => { + try { + const response = await fetch( + `${url_Backend_dwinzo}/api/V1/floorAssets/${projectId}/${versionId}`, + { + method: "GET", + headers: { + Authorization: "Bearer ", + "Content-Type": "application/json", + token: localStorage.getItem("token") || "", + refresh_token: localStorage.getItem("refreshToken") || "", + }, + } + ); + const newAccessToken = response.headers.get("x-access-token"); + if (newAccessToken) { + //console.log("New token received:", newAccessToken); + localStorage.setItem("token", newAccessToken); + } + + // console.log('response: ', response); + if (!response.ok) { + console.error("Failed to get assets"); + } + + const result = await response.json(); + + return result; + } catch (error) { + echo.error("Failed to get floor asset"); + if (error instanceof Error) { + console.log(error.message); + } else { + console.log("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/factoryBuilder/assest/floorAsset/setAssetsApi.ts b/app/src/services/factoryBuilder/asset/floorAsset/setAssetsApi.ts similarity index 100% rename from app/src/services/factoryBuilder/assest/floorAsset/setAssetsApi.ts rename to app/src/services/factoryBuilder/asset/floorAsset/setAssetsApi.ts diff --git a/app/src/services/factoryBuilder/floor/deleteFloorApi.ts b/app/src/services/factoryBuilder/floor/deleteFloorApi.ts new file mode 100644 index 0000000..3043646 --- /dev/null +++ b/app/src/services/factoryBuilder/floor/deleteFloorApi.ts @@ -0,0 +1,39 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const deleteFloorApi = async ( + projectId: string, + versionId: string, + floorUuid: string +) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/V1/deleteFloor`, { + method: "PATCH", + headers: { + Authorization: "Bearer ", + "Content-Type": "application/json", + token: localStorage.getItem("token") || "", + refresh_token: localStorage.getItem("refreshToken") || "", + }, + body: JSON.stringify({ projectId, versionId, floorUuid }), + }); + + const newAccessToken = response.headers.get("x-access-token"); + if (newAccessToken) { + localStorage.setItem("token", newAccessToken); + } + + if (!response.ok) { + console.error("Failed to delete floor:", response.statusText); + } + + const result = await response.json(); + return result; + } catch (error) { + echo.error("Failed to delete floor"); + if (error instanceof Error) { + console.log(error.message); + } else { + console.log("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/factoryBuilder/floor/getFloorsApi.ts b/app/src/services/factoryBuilder/floor/getFloorsApi.ts new file mode 100644 index 0000000..2659e61 --- /dev/null +++ b/app/src/services/factoryBuilder/floor/getFloorsApi.ts @@ -0,0 +1,37 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const getFloorsApi = async ( + projectId: string, + versionId: string, +) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/V1/floors/${projectId}/${versionId}`, { + method: "GET", + headers: { + Authorization: "Bearer ", + "Content-Type": "application/json", + token: localStorage.getItem("token") || "", + refresh_token: localStorage.getItem("refreshToken") || "", + }, + }); + + const newAccessToken = response.headers.get("x-access-token"); + if (newAccessToken) { + localStorage.setItem("token", newAccessToken); + } + + if (!response.ok) { + console.error("Failed to get floors:", response.statusText); + } + + const result = await response.json(); + return result; + } catch (error) { + echo.error("Failed to get floors"); + if (error instanceof Error) { + console.log(error.message); + } else { + console.log("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/factoryBuilder/floor/upsertFloorApi.ts b/app/src/services/factoryBuilder/floor/upsertFloorApi.ts new file mode 100644 index 0000000..45590c1 --- /dev/null +++ b/app/src/services/factoryBuilder/floor/upsertFloorApi.ts @@ -0,0 +1,39 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const upsertFloorApi = async ( + projectId: string, + versionId: string, + floorData: Floor +) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/V1/UpsertFloor`, { + method: "POST", + headers: { + Authorization: "Bearer ", + "Content-Type": "application/json", + token: localStorage.getItem("token") || "", + refresh_token: localStorage.getItem("refreshToken") || "", + }, + body: JSON.stringify({ projectId, versionId, floorData }), + }); + + const newAccessToken = response.headers.get("x-access-token"); + if (newAccessToken) { + localStorage.setItem("token", newAccessToken); + } + + if (!response.ok) { + console.error("Failed to upsert floor:", response.statusText); + } + + const result = await response.json(); + return result; + } catch (error) { + echo.error("Failed to upsert floor"); + if (error instanceof Error) { + console.log(error.message); + } else { + console.log("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/factoryBuilder/lines/deleteLayerApi.ts b/app/src/services/factoryBuilder/lines/deleteLayerApi.ts deleted file mode 100644 index f271061..0000000 --- a/app/src/services/factoryBuilder/lines/deleteLayerApi.ts +++ /dev/null @@ -1,34 +0,0 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; - -export const deleteLayer = async (organization: string, layer: number) => { - try { - const response = await fetch(`${url_Backend_dwinzo}/api/v1/deleteLayer`, { - method: "POST", - headers: { - Authorization: "Bearer ", - "Content-Type": "application/json", - token: localStorage.getItem("token") || "", - refresh_token: localStorage.getItem("refreshToken") || "", - }, - body: JSON.stringify({ organization, layer }), - }); - const newAccessToken = response.headers.get("x-access-token"); - if (newAccessToken) { - //console.log("New token received:", newAccessToken); - localStorage.setItem("token", newAccessToken); - } - if (!response.ok) { - console.error("Failed to delete line"); - } - - const result = await response.json(); - return result; - } catch (error) { - echo.error("Failed to delete line"); - if (error instanceof Error) { - console.log(error.message); - } else { - console.log("An unknown error occurred"); - } - } -}; diff --git a/app/src/services/factoryBuilder/lines/deleteLineApi.ts b/app/src/services/factoryBuilder/lines/deleteLineApi.ts deleted file mode 100644 index bd04ee4..0000000 --- a/app/src/services/factoryBuilder/lines/deleteLineApi.ts +++ /dev/null @@ -1,34 +0,0 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; - -export const deleteLineApi = async (organization: string, line: Object) => { - try { - const response = await fetch(`${url_Backend_dwinzo}/api/v1/deleteLine`, { - method: "DELETE", - headers: { - Authorization: "Bearer ", - "Content-Type": "application/json", - token: localStorage.getItem("token") || "", - refresh_token: localStorage.getItem("refreshToken") || "", - }, - body: JSON.stringify({ organization, line }), - }); - const newAccessToken = response.headers.get("x-access-token"); - if (newAccessToken) { - //console.log("New token received:", newAccessToken); - localStorage.setItem("token", newAccessToken); - } - if (!response.ok) { - console.error("Failed to delete line"); - } - - const result = await response.json(); - return result; - } catch (error) { - echo.error("Failed to delete line"); - if (error instanceof Error) { - console.log(error.message); - } else { - console.log("An unknown error occurred"); - } - } -}; diff --git a/app/src/services/factoryBuilder/lines/deletePointApi.ts b/app/src/services/factoryBuilder/lines/deletePointApi.ts deleted file mode 100644 index bc5b2d0..0000000 --- a/app/src/services/factoryBuilder/lines/deletePointApi.ts +++ /dev/null @@ -1,35 +0,0 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; - -export const deletePointApi = async (organization: string, uuid: string) => { - try { - const response = await fetch(`${url_Backend_dwinzo}/api/v1/deletePoint`, { - method: "DELETE", - headers: { - Authorization: "Bearer ", - "Content-Type": "application/json", - token: localStorage.getItem("token") || "", - refresh_token: localStorage.getItem("refreshToken") || "", - }, - body: JSON.stringify({ organization, uuid }), - }); - const newAccessToken = response.headers.get("x-access-token"); - if (newAccessToken) { - //console.log("New token received:", newAccessToken); - localStorage.setItem("token", newAccessToken); - } - - if (!response.ok) { - console.error("Failed to delete point"); - } - - const result = await response.json(); - return result; - } catch (error) { - echo.error("Failed to delete point"); - if (error instanceof Error) { - console.log(error.message); - } else { - console.log("An unknown error occurred"); - } - } -}; diff --git a/app/src/services/factoryBuilder/lines/getLinesApi.ts b/app/src/services/factoryBuilder/lines/getLinesApi.ts deleted file mode 100644 index ca520f1..0000000 --- a/app/src/services/factoryBuilder/lines/getLinesApi.ts +++ /dev/null @@ -1,38 +0,0 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; - -export const getLines = async (organization: string, projectId?: string, versionId?: string) => { - try { - const response = await fetch( - `${url_Backend_dwinzo}/api/V1/lines/${projectId}/${versionId}`, - { - method: "GET", - headers: { - Authorization: "Bearer ", - "Content-Type": "application/json", - token: localStorage.getItem("token") || "", - refresh_token: localStorage.getItem("refreshToken") || "", - }, - } - ); - const newAccessToken = response.headers.get("x-access-token"); - if (newAccessToken) { - //console.log("New token received:", newAccessToken); - localStorage.setItem("token", newAccessToken); - } - - if (!response.ok) { - console.error("Failed to get Lines"); - } - - const result = await response.json(); - // console.log('result: ', result); - return result; - } catch (error) { - echo.error("Failed to get Lines"); - if (error instanceof Error) { - console.log(error.message); - } else { - console.log("An unknown error occurred"); - } - } -}; diff --git a/app/src/services/factoryBuilder/lines/setLineApi.ts b/app/src/services/factoryBuilder/lines/setLineApi.ts deleted file mode 100644 index 9269514..0000000 --- a/app/src/services/factoryBuilder/lines/setLineApi.ts +++ /dev/null @@ -1,41 +0,0 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; - -export const setLine = async ( - organization: string, - layer: number, - line: Object, - type: string -) => { - try { - const response = await fetch(`${url_Backend_dwinzo}/api/v1/setLine`, { - method: "POST", - headers: { - Authorization: "Bearer ", - "Content-Type": "application/json", - token: localStorage.getItem("token") || "", - refresh_token: localStorage.getItem("refreshToken") || "", - }, - body: JSON.stringify({ organization, layer, line, type }), - }); - - const newAccessToken = response.headers.get("x-access-token"); - if (newAccessToken) { - //console.log("New token received:", newAccessToken); - localStorage.setItem("token", newAccessToken); - } - - if (!response.ok) { - console.error("Failed to set line"); - } - - const result = await response.json(); - return result; - } catch (error) { - echo.error("Failed to set line"); - if (error instanceof Error) { - console.log(error.message); - } else { - console.log("An unknown error occurred"); - } - } -}; diff --git a/app/src/services/factoryBuilder/lines/updatePointApi.ts b/app/src/services/factoryBuilder/lines/updatePointApi.ts deleted file mode 100644 index ae1b917..0000000 --- a/app/src/services/factoryBuilder/lines/updatePointApi.ts +++ /dev/null @@ -1,38 +0,0 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; - -export const updatePoint = async ( - organization: string, - position: Object, - uuid: string -) => { - try { - const response = await fetch(`${url_Backend_dwinzo}/api/v1/updatePoint`, { - method: "POST", - headers: { - Authorization: "Bearer ", - "Content-Type": "application/json", - token: localStorage.getItem("token") || "", - refresh_token: localStorage.getItem("refreshToken") || "", - }, - body: JSON.stringify({ organization, position, uuid }), - }); - const newAccessToken = response.headers.get("x-access-token"); - if (newAccessToken) { - //console.log("New token received:", newAccessToken); - localStorage.setItem("token", newAccessToken); - } - if (!response.ok) { - console.error("Failed to update point"); - } - - const result = await response.json(); - return result; - } catch (error) { - echo.error("Failed to update point"); - if (error instanceof Error) { - console.log(error.message); - } else { - console.log("An unknown error occurred"); - } - } -}; diff --git a/app/src/services/factoryBuilder/wall/deleteWallApi.ts b/app/src/services/factoryBuilder/wall/deleteWallApi.ts new file mode 100644 index 0000000..8a91d11 --- /dev/null +++ b/app/src/services/factoryBuilder/wall/deleteWallApi.ts @@ -0,0 +1,39 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const deleteWallApi = async ( + projectId: string, + versionId: string, + wallUuid: string +) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/V1/deleteWall`, { + method: "PATCH", + headers: { + Authorization: "Bearer ", + "Content-Type": "application/json", + token: localStorage.getItem("token") || "", + refresh_token: localStorage.getItem("refreshToken") || "", + }, + body: JSON.stringify({ projectId, versionId, wallUuid }), + }); + + const newAccessToken = response.headers.get("x-access-token"); + if (newAccessToken) { + localStorage.setItem("token", newAccessToken); + } + + if (!response.ok) { + console.error("Failed to delete wall:", response.statusText); + } + + const result = await response.json(); + return result; + } catch (error) { + echo.error("Failed to delete wall"); + if (error instanceof Error) { + console.log(error.message); + } else { + console.log("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/factoryBuilder/wall/getWallsApi.ts b/app/src/services/factoryBuilder/wall/getWallsApi.ts new file mode 100644 index 0000000..b3f94b0 --- /dev/null +++ b/app/src/services/factoryBuilder/wall/getWallsApi.ts @@ -0,0 +1,37 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const getWallsApi = async ( + projectId: string, + versionId: string, +) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/V1/Walls/${projectId}/${versionId}`, { + method: "GET", + headers: { + Authorization: "Bearer ", + "Content-Type": "application/json", + token: localStorage.getItem("token") || "", + refresh_token: localStorage.getItem("refreshToken") || "", + }, + }); + + const newAccessToken = response.headers.get("x-access-token"); + if (newAccessToken) { + localStorage.setItem("token", newAccessToken); + } + + if (!response.ok) { + console.error("Failed to get walls:", response.statusText); + } + + const result = await response.json(); + return result; + } catch (error) { + echo.error("Failed to get walls"); + if (error instanceof Error) { + console.log(error.message); + } else { + console.log("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/factoryBuilder/wall/upsertWallApi.ts b/app/src/services/factoryBuilder/wall/upsertWallApi.ts new file mode 100644 index 0000000..023da68 --- /dev/null +++ b/app/src/services/factoryBuilder/wall/upsertWallApi.ts @@ -0,0 +1,39 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const upsertWallApi = async ( + projectId: string, + versionId: string, + wallData: Wall +) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/V1/UpsertWall`, { + method: "POST", + headers: { + Authorization: "Bearer ", + "Content-Type": "application/json", + token: localStorage.getItem("token") || "", + refresh_token: localStorage.getItem("refreshToken") || "", + }, + body: JSON.stringify({ projectId, versionId, wallData }), + }); + + const newAccessToken = response.headers.get("x-access-token"); + if (newAccessToken) { + localStorage.setItem("token", newAccessToken); + } + + if (!response.ok) { + console.error("Failed to upsert wall:", response.statusText); + } + + const result = await response.json(); + return result; + } catch (error) { + echo.error("Failed to upsert wall"); + if (error instanceof Error) { + console.log(error.message); + } else { + console.log("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/factoryBuilder/zone/deleteZoneApi.ts b/app/src/services/factoryBuilder/zone/deleteZoneApi.ts new file mode 100644 index 0000000..87b80dd --- /dev/null +++ b/app/src/services/factoryBuilder/zone/deleteZoneApi.ts @@ -0,0 +1,39 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const deleteZoneApi = async ( + projectId: string, + versionId: string, + zoneUuid: string +) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/V1/zones/delete`, { + method: "PATCH", + headers: { + Authorization: "Bearer ", + "Content-Type": "application/json", + token: localStorage.getItem("token") || "", + refresh_token: localStorage.getItem("refreshToken") || "", + }, + body: JSON.stringify({ projectId, versionId, zoneUuid }), + }); + + const newAccessToken = response.headers.get("x-access-token"); + if (newAccessToken) { + localStorage.setItem("token", newAccessToken); + } + + if (!response.ok) { + console.error("Failed to delete zone:", response.statusText); + } + + const result = await response.json(); + return result; + } catch (error) { + echo.error("Failed to delete zone"); + if (error instanceof Error) { + console.log(error.message); + } else { + console.log("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/factoryBuilder/zone/getZonesApi.ts b/app/src/services/factoryBuilder/zone/getZonesApi.ts new file mode 100644 index 0000000..f0ea687 --- /dev/null +++ b/app/src/services/factoryBuilder/zone/getZonesApi.ts @@ -0,0 +1,37 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const getZonesApi = async ( + projectId: string, + versionId: string, +) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/V1/zones/${projectId}/${versionId}`, { + method: "GET", + headers: { + Authorization: "Bearer ", + "Content-Type": "application/json", + token: localStorage.getItem("token") || "", + refresh_token: localStorage.getItem("refreshToken") || "", + }, + }); + + const newAccessToken = response.headers.get("x-access-token"); + if (newAccessToken) { + localStorage.setItem("token", newAccessToken); + } + + if (!response.ok) { + console.error("Failed to get zones:", response.statusText); + } + + const result = await response.json(); + return result; + } catch (error) { + echo.error("Failed to get zones"); + if (error instanceof Error) { + console.log(error.message); + } else { + console.log("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/factoryBuilder/zone/upsertZoneApi.ts b/app/src/services/factoryBuilder/zone/upsertZoneApi.ts new file mode 100644 index 0000000..e19eae8 --- /dev/null +++ b/app/src/services/factoryBuilder/zone/upsertZoneApi.ts @@ -0,0 +1,39 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const upsertZoneApi = async ( + projectId: string, + versionId: string, + zoneData: Zone +) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/V1/upsertZone`, { + method: "POST", + headers: { + Authorization: "Bearer ", + "Content-Type": "application/json", + token: localStorage.getItem("token") || "", + refresh_token: localStorage.getItem("refreshToken") || "", + }, + body: JSON.stringify({ projectId, versionId, zoneData }), + }); + + const newAccessToken = response.headers.get("x-access-token"); + if (newAccessToken) { + localStorage.setItem("token", newAccessToken); + } + + if (!response.ok) { + console.error("Failed to upsert zone:", response.statusText); + } + + const result = await response.json(); + return result; + } catch (error) { + echo.error("Failed to upsert zone"); + if (error instanceof Error) { + console.log(error.message); + } else { + console.log("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/factoryBuilder/zones/deleteZoneApi.ts b/app/src/services/factoryBuilder/zones/deleteZoneApi.ts deleted file mode 100644 index f0ee326..0000000 --- a/app/src/services/factoryBuilder/zones/deleteZoneApi.ts +++ /dev/null @@ -1,39 +0,0 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; - -export const deleteZonesApi = async ( - userId: string, - organization: string, - zoneUuid: string -) => { - try { - const response = await fetch(`${url_Backend_dwinzo}/api/v1/setLine`, { - method: "POST", - headers: { - Authorization: "Bearer ", - "Content-Type": "application/json", - token: localStorage.getItem("token") || "", - refresh_token: localStorage.getItem("refreshToken") || "", - }, - body: JSON.stringify({ userId, organization, zoneUuid }), - }); - const newAccessToken = response.headers.get("x-access-token"); - if (newAccessToken) { - //console.log("New token received:", newAccessToken); - localStorage.setItem("token", newAccessToken); - } - if (!response.ok) { - console.error("Failed to delete zone"); - } - - const result = await response.json(); - - return result; - } catch (error) { - echo.error("Failed to delete zone"); - if (error instanceof Error) { - console.log(error.message); - } else { - console.log("An unknown error occurred"); - } - } -}; diff --git a/app/src/services/factoryBuilder/zones/getZonesApi.ts b/app/src/services/factoryBuilder/zones/getZonesApi.ts deleted file mode 100644 index 39f91a3..0000000 --- a/app/src/services/factoryBuilder/zones/getZonesApi.ts +++ /dev/null @@ -1,40 +0,0 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -// let url_Backend_dwinzo = `http://192.168.0.102:5000`; - -export const getZonesApi = async (organization: string, projectId?: string, versionId?: string) => { - try { - const response = await fetch( - `${url_Backend_dwinzo}/api/V1/zones/${projectId}/${versionId}`, - { - method: "GET", - headers: { - Authorization: "Bearer ", - "Content-Type": "application/json", - token: localStorage.getItem("token") || "", - refresh_token: localStorage.getItem("refreshToken") || "", - }, - } - ); - const newAccessToken = response.headers.get("x-access-token"); - if (newAccessToken) { - //console.log("New token received:", newAccessToken); - localStorage.setItem("token", newAccessToken); - } - - if (!response.ok) { - console.error("Failed to get Zones"); - } - - const result = await response.json(); - // console.log('result:zone ', result); - - return result; - } catch (error) { - echo.error("Failed to get zone data"); - if (error instanceof Error) { - console.log(error.message); - } else { - console.log("An unknown error occurred"); - } - } -}; diff --git a/app/src/services/factoryBuilder/zones/setZonesApi.ts b/app/src/services/factoryBuilder/zones/setZonesApi.ts deleted file mode 100644 index b5aa678..0000000 --- a/app/src/services/factoryBuilder/zones/setZonesApi.ts +++ /dev/null @@ -1,38 +0,0 @@ -let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; - -export const setZonesApi = async ( - userId: string, - organization: string, - zoneData: any -) => { - try { - const response = await fetch(`${url_Backend_dwinzo}/api/v1/setLine`, { - method: "POST", - headers: { - Authorization: "Bearer ", - "Content-Type": "application/json", - token: localStorage.getItem("token") || "", - refresh_token: localStorage.getItem("refreshToken") || "", - }, - body: JSON.stringify({ userId, organization, zoneData }), - }); - const newAccessToken = response.headers.get("x-access-token"); - if (newAccessToken) { - //console.log("New token received:", newAccessToken); - localStorage.setItem("token", newAccessToken); - } - if (!response.ok) { - console.error("Failed to set zone"); - } - - const result = await response.json(); - return result; - } catch (error) { - echo.error("Failed to zone data"); - if (error instanceof Error) { - console.log(error.message); - } else { - console.log("An unknown error occurred"); - } - } -}; diff --git a/app/src/store/builder/store.ts b/app/src/store/builder/store.ts index 26bf8b8..1f237cb 100644 --- a/app/src/store/builder/store.ts +++ b/app/src/store/builder/store.ts @@ -523,6 +523,7 @@ export const useWidgetSubOption = create((set: any) => ({ widgetSubOption: "2D", setWidgetSubOption: (x: any) => set({ widgetSubOption: x }), })); + export const useLimitDistance = create((set: any) => ({ limitDistance: true, setLimitDistance: (x: any) => set({ limitDistance: x }), diff --git a/app/src/store/builder/useBuilderStore.ts b/app/src/store/builder/useBuilderStore.ts index 7803df9..6147c0c 100644 --- a/app/src/store/builder/useBuilderStore.ts +++ b/app/src/store/builder/useBuilderStore.ts @@ -9,6 +9,13 @@ interface BuilderState { snappedPosition: [number, number, number] | null; hoveredLine: [Point, Point] | null; + // Wall Asset + selectedWallAsset: Object3D | null; + deletableWallAsset: Object3D | null; + + // Floor Asset + selectedFloorAsset: Object3D | null; + // Wall Settings selectedWall: Object3D | null; wallThickness: number; @@ -16,6 +23,19 @@ interface BuilderState { outsideMaterial: string; insideMaterial: string; + // Floor Settings + selectedFloor: Object3D | null; + floorDepth: number; + isBeveled: boolean; + bevelStrength: number; + sideMaterial: string; + topMaterial: string; + + // Zone Settings + selectedZone: Object3D | null; + zoneHeight: number; + zoneColor: string; + // Decal Settings selectedDecal: Object3D | null; @@ -38,12 +58,31 @@ interface BuilderState { setSnappedPosition: (position: [number, number, number] | null) => void; setHoveredLine: (line: [Point, Point] | null) => void; + // Setters - Wall Asset + setSelectedWallAsset: (asset: Object3D | null) => void; + setDeletableWallAsset: (asset: Object3D | null) => void; + + // Setters - Floor Asset + setSelectedFloorAsset: (asset: Object3D | null) => void; + // Setters - Wall setSelectedWall: (wall: Object3D | null) => void; setWallThickness: (thickness: number) => void; setWallHeight: (height: number) => void; setWallMaterial: (material: string, side: 'inside' | 'outside') => void; + // Setters - Floor + setSelectedFloor: (floor: Object3D | null) => void; + setFloorDepth: (depth: number) => void; + setIsBeveled: (isBeveled: boolean) => void; + setBevelStrength: (strength: number) => void; + setFloorMaterial: (material: string, side: 'side' | 'top') => void; + + // Setters - Zone + setSelectedZone: (zone: Object3D | null) => void; + setZoneHeight: (height: number) => void; + setZoneColor: (color: string) => void; + // Setters - Decal setSelectedDecal: (decal: Object3D | null) => void; @@ -75,12 +114,28 @@ export const useBuilderStore = create()( snappedPosition: null, hoveredLine: null, + selectedWallAsset: null, + deletableWallAsset: null, + + selectedFloorAsset: null, + selectedWall: null, wallThickness: 0.5, wallHeight: 7, outsideMaterial: 'Default Material', insideMaterial: 'Material 1', + selectedFloor: null, + floorDepth: 0.1, + isBeveled: false, + bevelStrength: 5, + sideMaterial: 'Material 1', + topMaterial: 'Default Material', + + selectedZone: null, + zoneHeight: 7, + zoneColor: 'blue', + selectedDecal: null, selectedAisle: null, @@ -120,6 +175,28 @@ export const useBuilderStore = create()( }) }, + // === Setters: Wall Asset === + + setSelectedWallAsset(asset: Object3D | null) { + set((state) => { + state.selectedWallAsset = asset; + }); + }, + + setDeletableWallAsset(asset: Object3D | null) { + set((state) => { + state.deletableWallAsset = asset; + }); + }, + + // === Setters: Floor Asset === + + setSelectedFloorAsset(asset: Object3D | null) { + set((state) => { + state.selectedFloorAsset = asset; + }); + }, + // === Setters: Wall === setSelectedWall: (wall: Object3D | null) => { @@ -147,6 +224,58 @@ export const useBuilderStore = create()( }); }, + // === Setters: Floor === + setSelectedFloor: (floor: Object3D | null) => { + set((state) => { + state.selectedFloor = floor; + }); + }, + + setFloorDepth: (depth: number) => { + set((state) => { + state.floorDepth = depth; + }); + }, + + setIsBeveled: (isBeveled: boolean) => { + set((state) => { + state.isBeveled = isBeveled; + }); + }, + + setBevelStrength: (strength: number) => { + set((state) => { + state.bevelStrength = strength; + }); + }, + + setFloorMaterial: (material: string, side: 'side' | 'top') => { + set((state) => { + if (side === 'side') state.sideMaterial = material; + else state.topMaterial = material; + }); + }, + + // === Setters: Zone === + + setSelectedZone: (zone: Object3D | null) => { + set((state) => { + state.selectedZone = zone; + }); + }, + + setZoneHeight: (height: number) => { + set((state) => { + state.zoneHeight = height; + }); + }, + + setZoneColor: (color: string) => { + set((state) => { + state.zoneColor = color; + }); + }, + // === Setters: Decal === setSelectedDecal: (decal: Object3D | null) => { diff --git a/app/src/store/builder/useFloorStore.ts b/app/src/store/builder/useFloorStore.ts index 4c89333..efb431b 100644 --- a/app/src/store/builder/useFloorStore.ts +++ b/app/src/store/builder/useFloorStore.ts @@ -5,16 +5,32 @@ interface FloorStore { floors: Floor[]; setFloors: (floors: Floor[]) => void; addFloor: (floor: Floor) => void; - updateFloor: (uuid: string, updated: Partial) => void; + updateFloor: (uuid: string, updated: Partial) => Floor | undefined; + setFloorName: (uuid: string, name: string) => void; removeFloor: (uuid: string) => void; - removePointFromFloors: (pointUuid: string) => void; + removePoint: (pointUuid: string) => { removedFloors: Floor[], updatedFloors: Floor[] }; + removeFloorByPoints: (Points: [Point, Point]) => { removedFloors: Floor[], updatedFloors: Floor[] }; clearFloors: () => void; + setPosition: ( + pointUuid: string, + position: [number, number, number] + ) => Floor[] | []; setIsBeveled: (uuid: string, isBeveled: boolean) => void; setBevelStrength: (uuid: string, strength: number) => void; setDepth: (uuid: string, depth: number) => void; setMaterial: (uuid: string, sideMaterial: string, topMaterial: string) => void; + addDecal: (floors: string, decal: Decal) => void; + updateDecal: (decalUuid: string, decal: Decal) => void; + removeDecal: (decalUuid: string) => void; + updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void; + updateDecalRotation: (decalUuid: string, rotation: number) => void; + updateDecalScale: (decalUuid: string, scale: number) => void; getFloorById: (uuid: string) => Floor | undefined; + getFloorsByPointId: (uuid: string) => Floor[] | []; + getFloorByPoints: (points: Point[]) => Floor | undefined; + getFloorPointById: (uuid: string) => Point | undefined; + getConnectedPoints: (uuid: string) => Point[]; } export const createFloorStore = () => { @@ -30,10 +46,22 @@ export const createFloorStore = () => { state.floors.push(floor); }), - updateFloor: (uuid, updated) => set(state => { + updateFloor: (uuid, updated) => { + let updatedFloor: Floor | undefined; + set(state => { + const floor = state.floors.find(f => f.floorUuid === uuid); + if (floor) { + Object.assign(floor, updated); + updatedFloor = JSON.parse(JSON.stringify(floor)); + } + }); + return updatedFloor; + }, + + setFloorName: (uuid, name) => set(state => { const floor = state.floors.find(f => f.floorUuid === uuid); if (floor) { - Object.assign(floor, updated); + floor.floorName = name; } }), @@ -41,16 +69,97 @@ export const createFloorStore = () => { state.floors = state.floors.filter(f => f.floorUuid !== uuid); }), - removePointFromFloors: (pointUuid) => set(state => { - for (const floor of state.floors) { - floor.points = floor.points.filter(p => p.pointUuid !== pointUuid); - } - }), + removePoint: (pointUuid) => { + const removedFloors: Floor[] = []; + const updatedFloors: Floor[] = []; + + set(state => { + for (const floor of state.floors) { + const pointIndex = floor.points.findIndex(p => p.pointUuid === pointUuid); + if (pointIndex === -1) { + updatedFloors.push(JSON.parse(JSON.stringify(floor))); + continue; + } + + const remainingPoints = floor.points.filter(p => p.pointUuid !== pointUuid); + + if (remainingPoints.length <= 2) { + removedFloors.push(JSON.parse(JSON.stringify(floor))); + continue; + } + floor.points = remainingPoints; + updatedFloors.push(JSON.parse(JSON.stringify(floor))); + } + + state.floors = updatedFloors; + }); + + return { removedFloors, updatedFloors }; + }, + + removeFloorByPoints: ([pointA, pointB]) => { + const removedFloors: Floor[] = []; + const updatedFloors: Floor[] = []; + + set(state => { + + for (const floor of state.floors) { + const indices = floor.points.map((p, i) => ({ uuid: p.pointUuid, index: i })); + + const idxA = indices.find(i => i.uuid === pointA.pointUuid)?.index ?? -1; + const idxB = indices.find(i => i.uuid === pointB.pointUuid)?.index ?? -1; + + if (idxA === -1 || idxB === -1) { + updatedFloors.push(JSON.parse(JSON.stringify(floor))); + continue; + } + + const areAdjacent = + Math.abs(idxA - idxB) === 1 || + (idxA === 0 && idxB === floor.points.length - 1) || + (idxB === 0 && idxA === floor.points.length - 1); + + if (!areAdjacent) { + updatedFloors.push(JSON.parse(JSON.stringify(floor))); + continue; + } + + const remainingPoints = floor.points.filter( + p => p.pointUuid !== pointA.pointUuid && p.pointUuid !== pointB.pointUuid + ); + + if (remainingPoints.length > 2) { + floor.points = remainingPoints; + updatedFloors.push(JSON.parse(JSON.stringify(floor))); + } else { + removedFloors.push(JSON.parse(JSON.stringify(floor))); + } + } + + state.floors = updatedFloors; + }); + + return { removedFloors, updatedFloors }; + }, clearFloors: () => set(state => { state.floors = []; }), + setPosition: (pointUuid, position) => { + let updatedFloor: Floor[] = []; + set((state) => { + for (const floor of state.floors) { + const point = floor.points.find((p) => p.pointUuid === pointUuid); + if (point) { + point.position = position; + updatedFloor.push(JSON.parse(JSON.stringify(floor))); + } + } + }); + return updatedFloor; + }, + setIsBeveled: (uuid, isBeveled) => set(state => { const floor = state.floors.find(f => f.floorUuid === uuid); if (floor) { @@ -80,9 +189,96 @@ export const createFloorStore = () => { } }), + addDecal: (floorUuid, decal) => set(state => { + const floor = state.floors.find(f => f.floorUuid === floorUuid); + if (floor) { + floor.decals.push(decal); + } + }), + + updateDecal: (decalUuid, updatedDecal) => set(state => { + for (const floor of state.floors) { + const index = floor.decals.findIndex(d => d.decalUuid === decalUuid); + if (index !== -1) { + floor.decals[index] = updatedDecal; + break; + } + } + }), + + removeDecal: (decalUuid) => set(state => { + for (const floor of state.floors) { + floor.decals = floor.decals.filter(d => d.decalUuid !== decalUuid); + } + }), + + updateDecalPosition: (decalUuid, position) => set(state => { + for (const floor of state.floors) { + const decal = floor.decals.find(d => d.decalUuid === decalUuid); + if (decal) { + decal.decalPosition = position; + break; + } + } + }), + + updateDecalRotation: (decalUuid, rotation) => set(state => { + for (const floor of state.floors) { + const decal = floor.decals.find(d => d.decalUuid === decalUuid); + if (decal) { + decal.decalRotation = rotation; + break; + } + } + }), + + updateDecalScale: (decalUuid, scale) => set(state => { + for (const floor of state.floors) { + const decal = floor.decals.find(d => d.decalUuid === decalUuid); + if (decal) { + decal.decalScale = scale; + break; + } + } + }), + + getFloorById: (uuid) => { return get().floors.find(f => f.floorUuid === uuid); }, + + getFloorsByPointId: (pointUuid) => { + return get().floors.filter(floor => { + return floor.points.some(p => p.pointUuid === pointUuid); + }); + }, + + getFloorByPoints: (points) => { + return get().floors.find(floor => { + const floorPointIds = new Set(floor.points.map(p => p.pointUuid)); + const givenPointIds = new Set(points.map(p => p.pointUuid)); + return floorPointIds.size === givenPointIds.size && [...floorPointIds].every(id => givenPointIds.has(id)); + }); + }, + + getFloorPointById: (pointUuid) => { + for (const floor of get().floors) { + const point = floor.points.find(p => p.pointUuid === pointUuid); + if (point) return point; + } + return undefined; + }, + + getConnectedPoints: (pointUuid) => { + const connected: Point[] = []; + for (const floor of get().floors) { + if (floor.points.some(p => p.pointUuid === pointUuid)) { + connected.push(...floor.points.filter(p => p.pointUuid !== pointUuid)); + } + } + return connected; + } + })) ); }; diff --git a/app/src/store/builder/useWallAssetStore.ts b/app/src/store/builder/useWallAssetStore.ts index 408aade..3d4e89d 100644 --- a/app/src/store/builder/useWallAssetStore.ts +++ b/app/src/store/builder/useWallAssetStore.ts @@ -6,6 +6,7 @@ interface WallAssetStore { setWallAssets: (assets: WallAsset[]) => void; addWallAsset: (asset: WallAsset) => void; updateWallAsset: (uuid: string, updated: Partial) => void; + setWallAssetPosition: (uuid: string, position: [number, number, number]) => void; removeWallAsset: (uuid: string) => void; clearWallAssets: () => void; @@ -37,6 +38,13 @@ export const createWallAssetStore = () => { } }), + setWallAssetPosition: (uuid, position) => set(state => { + const asset = state.wallAssets.find(a => a.modelUuid === uuid); + if (asset) { + asset.position = position; + } + }), + removeWallAsset: (uuid) => set(state => { state.wallAssets = state.wallAssets.filter(a => a.modelUuid !== uuid); }), diff --git a/app/src/store/builder/useWallStore.ts b/app/src/store/builder/useWallStore.ts index 3547330..11f7130 100644 --- a/app/src/store/builder/useWallStore.ts +++ b/app/src/store/builder/useWallStore.ts @@ -5,7 +5,7 @@ interface WallStore { walls: Wall[]; setWalls: (walls: Wall[]) => void; addWall: (wall: Wall) => void; - updateWall: (uuid: string, updated: Partial) => void; + updateWall: (uuid: string, updated: Partial) => Wall | undefined; removeWall: (uuid: string) => void; clearWalls: () => void; removeWallByPoints: (Points: [Point, Point]) => Wall | undefined; @@ -17,11 +17,11 @@ interface WallStore { updateDecalScale: (decalUuid: string, scale: number) => void; removePoint: (pointUuid: string) => Wall[]; - setPosition: (pointUuid: string, position: [number, number, number]) => void; + setPosition: (pointUuid: string, position: [number, number, number]) => Wall[] | []; setLayer: (pointUuid: string, layer: number) => void; getWallById: (uuid: string) => Wall | undefined; - getWallByPointId: (uuid: string) => Wall | undefined; + getWallsByPointId: (uuid: string) => Wall[] | []; getWallByPoints: (points: Point[]) => Wall | undefined; getWallPointById: (uuid: string) => Point | undefined; getConnectedPoints: (uuid: string) => Point[]; @@ -40,12 +40,17 @@ export const createWallStore = () => { state.walls.push(wall); }), - updateWall: (uuid, updated) => set((state) => { - const wall = state.walls.find(w => w.wallUuid === uuid); - if (wall) { - Object.assign(wall, updated); - } - }), + updateWall: (uuid, updated) => { + let updatedWall: Wall | undefined; + set((state) => { + const wall = state.walls.find(w => w.wallUuid === uuid); + if (wall) { + Object.assign(wall, updated); + updatedWall = JSON.parse(JSON.stringify(wall)); + } + }); + return updatedWall; + }, removeWall: (uuid) => set((state) => { state.walls = state.walls.filter(w => w.wallUuid !== uuid); @@ -144,14 +149,19 @@ export const createWallStore = () => { return removedWalls; }, - setPosition: (pointUuid, position) => set((state) => { - for (const wall of state.walls) { - const point = wall.points.find(p => p.pointUuid === pointUuid); - if (point) { - point.position = position; + setPosition: (pointUuid, position) => { + let updatedWalls: Wall[] = []; + set((state) => { + for (const wall of state.walls) { + const point = wall.points.find(p => p.pointUuid === pointUuid); + if (point) { + point.position = position; + updatedWalls.push(wall); + } } - } - }), + }); + return updatedWalls; + }, setLayer: (pointUuid, layer) => set((state) => { for (const wall of state.walls) { @@ -166,13 +176,10 @@ export const createWallStore = () => { return get().walls.find(w => w.wallUuid === uuid); }, - getWallByPointId: (uuid) => { - for (const wall of get().walls) { - if (wall.points.some(p => p.pointUuid === uuid)) { - return wall; - } - } - return undefined; + getWallsByPointId: (uuid) => { + return get().walls.filter((a) => { + return a.points.some((p) => p.pointUuid === uuid); + }) }, getWallByPoints: (point) => { diff --git a/app/src/store/builder/useZoneStore.ts b/app/src/store/builder/useZoneStore.ts index ee3c736..1a93e57 100644 --- a/app/src/store/builder/useZoneStore.ts +++ b/app/src/store/builder/useZoneStore.ts @@ -6,13 +6,24 @@ interface ZoneStore { setZones: (zones: Zone[]) => void; addZone: (zone: Zone) => void; updateZone: (uuid: string, updated: Partial) => void; + setZoneName: (uuid: string, name: string) => void; + setZoneHeight: (uuid: string, height: number) => void; + setZoneColor: (uuid: string, color: string) => void; removeZone: (uuid: string) => void; - removePointFromZones: (pointUuid: string) => void; + removePoint: (pointUuid: string) => { removedZones: Zone[], updatedZones: Zone[] }; + removeZoneByPoints: (points: Point[]) => { removedZones: Zone[], updatedZones: Zone[] }; clearZones: () => void; + setPosition: ( + pointUuid: string, + position: [number, number, number] + ) => Zone[] | []; setViewPort: (uuid: string, position: [number, number, number], target: [number, number, number]) => void; - setColor: (uuid: string, color: string) => void; getZoneById: (uuid: string) => Zone | undefined; + getZonesByPointId: (uuid: string) => Zone[] | []; + getZoneByPoints: (points: Point[]) => Zone | undefined; + getZonePointById: (uuid: string) => Point | undefined; + getConnectedPoints: (uuid: string) => Point[]; } export const createZoneStore = () => { @@ -35,20 +46,122 @@ export const createZoneStore = () => { } }), + setZoneName: (uuid, name) => set(state => { + const zone = state.zones.find(z => z.zoneUuid === uuid); + if (zone) { + zone.zoneName = name; + } + }), + + setZoneHeight: (uuid, height) => set(state => { + const zone = state.zones.find(z => z.zoneUuid === uuid); + if (zone) { + zone.zoneHeight = height; + } + }), + + setZoneColor: (uuid, color) => set(state => { + const zone = state.zones.find(z => z.zoneUuid === uuid); + if (zone) { + zone.zoneColor = color; + } + }), + removeZone: (uuid) => set(state => { state.zones = state.zones.filter(z => z.zoneUuid !== uuid); }), - removePointFromZones: (pointUuid) => set(state => { - for (const zone of state.zones) { - zone.points = zone.points.filter(p => p.pointUuid !== pointUuid); - } - }), + removePoint: (pointUuid) => { + const removedZones: Zone[] = []; + const updatedZones: Zone[] = []; + + set(state => { + for (const zone of state.zones) { + const pointIndex = zone.points.findIndex(p => p.pointUuid === pointUuid); + if (pointIndex === -1) { + updatedZones.push(JSON.parse(JSON.stringify(zone))); + continue; + } + + const remainingPoints = zone.points.filter(p => p.pointUuid !== pointUuid); + + if (remainingPoints.length <= 2) { + removedZones.push(JSON.parse(JSON.stringify(zone))); + continue; + } + zone.points = remainingPoints; + updatedZones.push(JSON.parse(JSON.stringify(zone))); + } + + state.zones = updatedZones; + }); + + return { removedZones, updatedZones }; + }, + + removeZoneByPoints: ([pointA, pointB]) => { + const removedZones: Zone[] = []; + const updatedZones: Zone[] = []; + + set(state => { + + for (const zone of state.zones) { + const indices = zone.points.map((p, i) => ({ uuid: p.pointUuid, index: i })); + + const idxA = indices.find(i => i.uuid === pointA.pointUuid)?.index ?? -1; + const idxB = indices.find(i => i.uuid === pointB.pointUuid)?.index ?? -1; + + if (idxA === -1 || idxB === -1) { + updatedZones.push(JSON.parse(JSON.stringify(zone))); + continue; + } + + const areAdjacent = + Math.abs(idxA - idxB) === 1 || + (idxA === 0 && idxB === zone.points.length - 1) || + (idxB === 0 && idxA === zone.points.length - 1); + + if (!areAdjacent) { + updatedZones.push(JSON.parse(JSON.stringify(zone))); + continue; + } + + const remainingPoints = zone.points.filter( + p => p.pointUuid !== pointA.pointUuid && p.pointUuid !== pointB.pointUuid + ); + + if (remainingPoints.length > 2) { + zone.points = remainingPoints; + updatedZones.push(JSON.parse(JSON.stringify(zone))); + } else { + removedZones.push(JSON.parse(JSON.stringify(zone))); + } + } + + state.zones = updatedZones; + }); + + return { removedZones, updatedZones }; + }, clearZones: () => set(state => { state.zones = []; }), + setPosition: (pointUuid, position) => { + let updatedZone: Zone[] = []; + set((state) => { + for (const zone of state.zones) { + const point = zone.points.find((p) => p.pointUuid === pointUuid); + if (point) { + point.position = position; + updatedZone.push(JSON.parse(JSON.stringify(zone))); + } + } + }); + return updatedZone; + }, + setViewPort: (uuid, position, target) => set(state => { const zone = state.zones.find(z => z.zoneUuid === uuid); if (zone) { @@ -57,16 +170,42 @@ export const createZoneStore = () => { } }), - setColor: (uuid, color) => set(state => { - const zone = state.zones.find(z => z.zoneUuid === uuid); - if (zone) { - zone.zoneColor = color; - } - }), - getZoneById: (uuid) => { return get().zones.find(z => z.zoneUuid === uuid); }, + + getZonesByPointId: (pointUuid) => { + return get().zones.filter(zone => { + return zone.points.some(p => p.pointUuid === pointUuid); + }); + }, + + getZoneByPoints: (points) => { + return get().zones.find(zone => { + const zonePointIds = new Set(zone.points.map(p => p.pointUuid)); + const givenPointIds = new Set(points.map(p => p.pointUuid)); + return zonePointIds.size === givenPointIds.size && [...zonePointIds].every(id => givenPointIds.has(id)); + }); + }, + + getZonePointById: (pointUuid) => { + for (const zone of get().zones) { + const point = zone.points.find(p => p.pointUuid === pointUuid); + if (point) return point; + } + return undefined; + }, + + getConnectedPoints: (pointUuid) => { + const connected: Point[] = []; + for (const zone of get().zones) { + if (zone.points.some(p => p.pointUuid === pointUuid)) { + connected.push(...zone.points.filter(p => p.pointUuid !== pointUuid)); + } + } + return connected; + } + })) ); }; diff --git a/app/src/types/builderTypes.d.ts b/app/src/types/builderTypes.d.ts index bbc9b7b..de7586a 100644 --- a/app/src/types/builderTypes.d.ts +++ b/app/src/types/builderTypes.d.ts @@ -51,9 +51,11 @@ type Assets = Asset[]; interface WallAsset { modelUuid: string; modelName: string; + wallAssetType: string; assetId: string; wallUuid: string; position: [number, number, number]; + rotation: [number, number, number]; isLocked: boolean; isVisible: boolean; opacity: number; @@ -113,6 +115,7 @@ type Walls = Wall[]; interface Floor { floorUuid: string; + floorName: string; points: Point[]; sideMaterial: string; topMaterial: string; @@ -130,6 +133,7 @@ type Floors = Floor[]; interface Zone { zoneUuid: string; zoneName: string; + zoneHeight: number; zoneColor: string; points: Point[]; viewPortTarget: [number, number, number];