diff --git a/app/src/assets/gltf-glb/layouts/floorplane_1.glb b/app/src/assets/gltf-glb/layouts/floorplane_1.glb new file mode 100644 index 0000000..b60626a Binary files /dev/null and b/app/src/assets/gltf-glb/layouts/floorplane_1.glb differ diff --git a/app/src/assets/gltf-glb/layouts/floorplane_2.glb b/app/src/assets/gltf-glb/layouts/floorplane_2.glb new file mode 100644 index 0000000..78e0972 Binary files /dev/null and b/app/src/assets/gltf-glb/layouts/floorplane_2.glb differ diff --git a/app/src/components/footer/Footer.tsx b/app/src/components/footer/Footer.tsx index 39b1df0..c890087 100644 --- a/app/src/components/footer/Footer.tsx +++ b/app/src/components/footer/Footer.tsx @@ -7,59 +7,83 @@ import { CurserMiddleIcon, CurserRightIcon, } from "../icons/LogIcons"; +import ShortcutHelper from "./shortcutHelper"; +import { useShortcutStore } from "../../store/builder/store"; +import { usePlayButtonStore } from "../../store/usePlayButtonStore"; +import OuterClick from "../../utils/outerClick"; const Footer: React.FC = () => { const { logs, setIsLogListVisible } = useLogger(); const lastLog = logs.length > 0 ? logs[logs.length - 1] : null; + const { isPlaying } = usePlayButtonStore(); + const { showShortcuts, setShowShortcuts } = useShortcutStore(); + + OuterClick({ + contextClassName: ["shortcut-helper-overlay"], + setMenuVisible: () => setShowShortcuts(false), + }); + return ( -
-
-
-
- +
+
+
+
+
+ +
+
Selection
+
+
+
+ +
+
Rotate/Zoom
+
+
+
+ +
+
Pan/Context Menu
-
Selection
-
-
- + +
+
+
+
+
-
Rotate/Zoom
-
-
-
- +
+ V 0.01 +
+ +
-
Pan/Context Menu
-
-
-
-
- + {!isPlaying && ( +
+
-
- V 0.01 -
- -
-
-
+ )}
); }; diff --git a/app/src/components/footer/shortcutHelper.tsx b/app/src/components/footer/shortcutHelper.tsx new file mode 100644 index 0000000..8a15b2e --- /dev/null +++ b/app/src/components/footer/shortcutHelper.tsx @@ -0,0 +1,328 @@ +import React from "react"; + +import { + UndoIcon, + RedoIcon, + ESCIcon, + HelpIcon, + FindIcon, + InfoIcon, + CurserIcon, + DeleteIcon, + FreeHandIcon, + MeasurementToolIcon, + WallToolIcon, + ZoneToolIcon, + AisleToolIcon, + FloorToolIcon, + MoveIcon, + RotateIcon, + ToogleViewIcon, + UIVisiblityIcon, + FirstPersonViewIcon, + BuilderIcon, + SimulationIcon, + VisualizationIcon, + MarketplaceIcon, + CopyIcon, + PasteIcon, + DublicateIcon, + DuplicateInstanceIcon, + PlayIcon, + BrowserIcon, +} from "../icons/ShortcutIcons"; + +interface ShortcutItem { + keys: string[]; + name?: string; + description: string; + icon: any; +} + +interface ShortcutGroup { + category: string; + items: ShortcutItem[]; +} + +const ShortcutHelper = () => { + const shortcuts: ShortcutGroup[] = [ + { + category: "Essential", + items: [ + { + keys: ["CTRL", "+", "Z"], + name: "Undo", + description: "Undo Last action", + icon: , + }, + { + keys: ["CTRL", "+", "Y"], + name: "Redo", + description: "Redo Last action", + icon: , + }, + { + keys: ["ESC"], + name: "Escape", + description: "Reset to Cursor & Stop Playback", + icon: , + }, + { + keys: ["CTRL", "+", "H"], + name: "Help", + description: "Open Help", + icon: , + }, + { + keys: ["CTRL", "+", "F"], + name: "Find", + description: "Find / Search Functionality", + icon: , + }, + { + keys: ["CTRL", "+", "?"], + name: "Info", + description: "Show Shortcut Info", + icon: , + }, + ], + }, + { + category: "Tools", + items: [ + { + keys: ["V"], + name: "Cursor Tool", + description: "Activate Cursor tool", + icon: , + }, + { + keys: ["X"], + name: "Delete Tool", + description: "Activate Delete tool", + icon: , + }, + { + keys: ["H"], + name: "Freehand Tool", + description: "Activate Free-Hand tool", + icon: , + }, + { + keys: ["M"], + name: "Measurement Tool", + description: "Activate Measurement tool", + icon: , + }, + { + keys: ["Q", "OR", "6"], + name: "Wall Tool", + description: "Select Wall floor tool (2D)", + icon: , + }, + { + keys: ["E", "OR", "8"], + name: "Zone Tool", + description: "Select Draw zone tool (2D)", + icon: , + }, + { + keys: ["R", "OR", "7"], + name: "Aisle Tool", + description: "Select Aisle floor tool (2D)", + icon: , + }, + { + keys: ["T", "OR", "9"], + name: "Floor Tool", + description: "Select Draw floor tool (2D)", + icon: , + }, + { + keys: ["G"], + name: "Move Asset", + description: "Move Selected Asset", + icon: , + }, + { + keys: ["R"], + name: "Rotate Asset", + description: "Rotate Selected Asset", + icon: , + }, + ], + }, + { + category: "View & Navigation", + items: [ + { + keys: ["TAB"], + name: "Toggle View", + description: "Toggle between 2D & 3D views (Builder)", + icon: , + }, + { + keys: ["CTRL", "+", "\\"], + name: "Toggle UI", + description: "Toggle UI Visibility", + icon: , + }, + { + keys: ["CTRL", "+", "["], + name: "Toggle UI", + description: "Left Sidebar Visibility", + icon: , + }, + { + keys: ["CTRL", "+", "]"], + name: "Toggle UI", + description: "Right Sidebar Visibility", + icon: , + }, + { + keys: ["/"], + name: "First Person View", + description: "Switch to First-person View", + icon: , + }, + ], + }, + { + category: "Module Switching", + items: [ + { + keys: ["1"], + name: "Builder", + description: "Switch to Builder module", + icon: , + }, + { + keys: ["2"], + name: "Simulation", + description: "Switch to Simulation module", + icon: , + }, + { + keys: ["3"], + name: "Visualization", + description: "Switch to Visualization module", + icon: , + }, + { + keys: ["4"], + name: "Marketplace", + description: "Switch to Marketplace module", + icon: , + }, + ], + }, + { + category: "Selection", + items: [ + { + keys: ["CTRL", "+", "C"], + name: "Copy", + description: "Copy an Asset", + icon: , + }, + { + keys: ["CTRL", "+", "V"], + name: "Paste", + description: "Paste an Asset", + icon: , + }, + { + keys: ["CTRL", "+", "D"], + name: "Duplicate", + description: "Duplicate an Asset", + icon: , + }, + { + keys: ["ALT", "+", "D"], + name: "Duplicate (Instance)", + description: "Duplicate an Instanced Asset", + icon: , + }, + ], + }, + { + category: "Simulation", + items: [ + { + keys: ["CTRL", "+", "P"], + name: "Play", + description: "Play Simulation", + icon: , + }, + ], + }, + { + category: "Miscellaneous", + items: [ + { + keys: ["F5", "F11", "F12", "CTRL", "+", "R"], + name: "Browser Defaults", + description: "Reserved for browser defaults", + icon: , + }, + ], + }, + ]; + + const [activeCategory, setActiveCategory] = + React.useState("Essential"); + + const activeShortcuts = + shortcuts.find((group) => group.category === activeCategory)?.items || []; + + return ( +
+
+
+ {shortcuts.map((group) => ( + + ))} +
+
+ +
+ {activeShortcuts.map((item) => ( +
+
+
{item.icon}
+
+
{item.name}
+
{item.description}
+
+
+
+ {item.keys.map((key, i) => ( + + {key} + + ))} +
+
+ ))} +
+
+ ); +}; + +export default ShortcutHelper; diff --git a/app/src/components/icons/ExportCommonIcons.tsx b/app/src/components/icons/ExportCommonIcons.tsx index 9b66849..f20db7b 100644 --- a/app/src/components/icons/ExportCommonIcons.tsx +++ b/app/src/components/icons/ExportCommonIcons.tsx @@ -947,3 +947,22 @@ export const ErrorIcon = () => { ); }; + +export const LocationIcon = () => { + return ( + + + + ); +}; diff --git a/app/src/components/icons/RealTimeVisulationIcons.tsx b/app/src/components/icons/RealTimeVisulationIcons.tsx index 84d8cca..c38560d 100644 --- a/app/src/components/icons/RealTimeVisulationIcons.tsx +++ b/app/src/components/icons/RealTimeVisulationIcons.tsx @@ -166,3 +166,6 @@ export function StockIncreseIcon() { ); } + + + diff --git a/app/src/components/icons/ShortcutIcons.tsx b/app/src/components/icons/ShortcutIcons.tsx new file mode 100644 index 0000000..b20948a --- /dev/null +++ b/app/src/components/icons/ShortcutIcons.tsx @@ -0,0 +1,827 @@ +export function UndoIcon() { + return ( + + + + ); +} + +export function RedoIcon() { + return ( + + + + ); +} + +export function ESCIcon() { + return ( + + + + + + ); +} + +export function HelpIcon() { + return ( + + + + ); +} + +export function FindIcon() { + return ( + + + + ); +} + +export function InfoIcon() { + return ( + + + + + + ); +} + +export function CurserIcon() { + return ( + + + + ); +} + +export function DeleteIcon() { + return ( + + + + ); +} + +export function FreeHandIcon() { + return ( + + + + ); +} + +export function MeasurementToolIcon() { + return ( + + + + + + ); +} + +export function WallToolIcon() { + return ( + + + + + + + + + + + + + + + + + + ); +} + +export function ZoneToolIcon() { + return ( + + + + + + + + + + + + + + + + + + ); +} + +export function AisleToolIcon() { + return ( + + + + + + + + + ); +} + +export function FloorToolIcon() { + return ( + + + + + + + + + + ); +} + +export function MoveIcon() { + return ( + + + + ); +} + +export function RotateIcon() { + return ( + + + + + + + ); +} + +export function ToogleViewIcon() { + return ( + + + + ); +} + +export function UIVisiblityIcon() { + return ( + + + + + ); +} + +export function FirstPersonViewIcon() { + return ( + + + + + + + + + ); +} + +export function BuilderIcon() { + return ( + + + + ); +} + +export function SimulationIcon() { + return ( + + + + + + + + + + ); +} + +export function VisualizationIcon() { + return ( + + + + + + ); +} + +export function MarketplaceIcon() { + return ( + + + + + + + ); +} + +export function CopyIcon() { + return ( + + + + ); +} + +export function PasteIcon() { + return ( + + + + ); +} + +export function DublicateIcon() { + return ( + + + + ); +} + +export function DuplicateInstanceIcon() { + return ( + + + + ); +} + +export function PlayIcon() { + return ( + + + + ); +} + +export function BrowserIcon() { + return ( + + + + ); +} diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index b23ce58..391675b 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import Search from "../../ui/inputs/Search"; import { getCategoryAsset } from "../../../services/factoryBuilder/assest/assets/getCategoryAsset"; import { fetchAssets } from "../../../services/marketplace/fetchAssets"; -import { useSelectedItem } from "../../../store/store"; +import { useSelectedItem } from "../../../store/builder/store"; // images ------------------- import vehicle from "../../../assets/image/categories/vehicles.png"; @@ -139,6 +139,16 @@ const Assets: React.FC = () => { src={asset?.thumbnail} alt={asset.filename} className="asset-image" + onPointerDown={() => { + setSelectedItem({ + name: asset.filename, + id: asset.AssetID, + type: + asset.type === "undefined" + ? undefined + : asset.type + }); + }} />
@@ -194,6 +204,8 @@ const Assets: React.FC = () => { asset.type === "undefined" ? undefined : asset.type, + category: asset.category, + subCategory: asset.subCategory }); }} /> diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets.tsx index 655e641..89138c1 100644 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets.tsx @@ -2,7 +2,7 @@ import ToggleHeader from "../../../../ui/inputs/ToggleHeader"; import Widgets2D from "./Widgets2D"; import Widgets3D from "./Widgets3D"; import WidgetsFloating from "./WidgetsFloating"; -import { useWidgetSubOption } from "../../../../../store/store"; +import { useWidgetSubOption } from "../../../../../store/builder/store"; const Widgets = () => { const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption(); diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets3D.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets3D.tsx index 7457648..f3cf4f1 100644 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets3D.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/Widgets3D.tsx @@ -2,7 +2,7 @@ import widget1 from "../../../../../assets/image/3D/ProductionCapacity.png"; import widget2 from "../../../../../assets/image/3D/ReturnOfInvestment.png"; import widget3 from "../../../../../assets/image/3D/StateWorking.png"; import widget4 from "../../../../../assets/image/3D/Throughput.png"; -import { useAsset3dWidget } from "../../../../../store/store"; +import { useAsset3dWidget } from "../../../../../store/builder/store"; const Widgets3D = () => { const widgets = [ { name: "Widget 1", img: widget1 }, diff --git a/app/src/components/layout/sidebarRight/Header.tsx b/app/src/components/layout/sidebarRight/Header.tsx index 9bfa4df..f4fa0cf 100644 --- a/app/src/components/layout/sidebarRight/Header.tsx +++ b/app/src/components/layout/sidebarRight/Header.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import orgImg from "../../../assets/image/orgTemp.png"; -import { useActiveUsers, useCamMode } from "../../../store/store"; +import { useActiveUsers, useCamMode } from "../../../store/builder/store"; import { ActiveUser } from "../../../types/users"; import CollaborationPopup from "../../templates/CollaborationPopup"; import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor"; diff --git a/app/src/components/layout/sidebarRight/SideBarRight.tsx b/app/src/components/layout/sidebarRight/SideBarRight.tsx index d643d69..2c54666 100644 --- a/app/src/components/layout/sidebarRight/SideBarRight.tsx +++ b/app/src/components/layout/sidebarRight/SideBarRight.tsx @@ -13,7 +13,9 @@ import useToggleStore from "../../../store/useUIToggleStore"; import Visualization from "./visualization/Visualization"; import Analysis from "./analysis/Analysis"; import Simulations from "./simulation/Simulations"; -import { useSelectedFloorItem } from "../../../store/store"; +import useVersionHistoryStore, { + useSelectedFloorItem, +} from "../../../store/builder/store"; import { useSelectedEventData, useSelectedEventSphere, @@ -22,6 +24,7 @@ import GlobalProperties from "./properties/GlobalProperties"; import AsstePropertiies from "./properties/AssetProperties"; import ZoneProperties from "./properties/ZoneProperties"; import EventProperties from "./properties/eventProperties/EventProperties"; +import VersionHistory from "./versionHisory/VersionHistory"; const SideBarRight: React.FC = () => { const { activeModule } = useModuleStore(); @@ -30,6 +33,7 @@ const SideBarRight: React.FC = () => { const { selectedFloorItem } = useSelectedFloorItem(); const { selectedEventData } = useSelectedEventData(); const { selectedEventSphere } = useSelectedEventSphere(); + const { viewVersionHistory, setVersionHistory } = useVersionHistoryStore(); // Reset activeList whenever activeModule changes useEffect(() => { @@ -64,7 +68,10 @@ const SideBarRight: React.FC = () => { className={`sidebar-action-list ${ subModule === "properties" ? "active" : "" }`} - onClick={() => setSubModule("properties")} + onClick={() => { + setSubModule("properties"); + setVersionHistory(false); + }} >
properties
@@ -76,7 +83,10 @@ const SideBarRight: React.FC = () => { className={`sidebar-action-list ${ subModule === "simulations" ? "active" : "" }`} - onClick={() => setSubModule("simulations")} + onClick={() => { + setSubModule("simulations"); + setVersionHistory(false); + }} >
simulations
@@ -85,7 +95,10 @@ const SideBarRight: React.FC = () => { className={`sidebar-action-list ${ subModule === "mechanics" ? "active" : "" }`} - onClick={() => setSubModule("mechanics")} + onClick={() => { + setSubModule("mechanics"); + setVersionHistory(false); + }} >
mechanics
@@ -94,7 +107,10 @@ const SideBarRight: React.FC = () => { className={`sidebar-action-list ${ subModule === "analysis" ? "active" : "" }`} - onClick={() => setSubModule("analysis")} + onClick={() => { + setSubModule("analysis"); + setVersionHistory(false); + }} >
analysis
@@ -103,8 +119,18 @@ const SideBarRight: React.FC = () => { )}
)} + + {toggleUIRight && viewVersionHistory && ( +
+
+ +
+
+ )} + {/* process builder */} {toggleUIRight && + !viewVersionHistory && subModule === "properties" && activeModule !== "visualization" && !selectedFloorItem && ( @@ -115,6 +141,7 @@ const SideBarRight: React.FC = () => {
)} {toggleUIRight && + !viewVersionHistory && subModule === "properties" && activeModule !== "visualization" && selectedFloorItem && ( @@ -124,7 +151,9 @@ const SideBarRight: React.FC = () => {
)} + {toggleUIRight && + !viewVersionHistory && subModule === "zoneProperties" && (activeModule === "builder" || activeModule === "simulation") && (
@@ -134,7 +163,7 @@ const SideBarRight: React.FC = () => {
)} {/* simulation */} - {toggleUIRight && activeModule === "simulation" && ( + {toggleUIRight && !viewVersionHistory && activeModule === "simulation" && ( <> {subModule === "simulations" && (
diff --git a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx index b81465d..85a7539 100644 --- a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx @@ -1,11 +1,10 @@ -import React, { useEffect, useState } from "react"; +import React, { useState } from "react"; import InputToggle from "../../../ui/inputs/InputToggle"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; import { RemoveIcon } from "../../../icons/ExportCommonIcons"; import PositionInput from "../customInput/PositionInputs"; import RotationInput from "../customInput/RotationInput"; -import { useSelectedFloorItem } from "../../../../store/store"; -import * as THREE from "three"; +import { useSelectedFloorItem, useObjectPosition, useObjectRotation } from "../../../../store/builder/store"; interface UserData { id: number; // Unique identifier for the user data @@ -17,6 +16,8 @@ const AssetProperties: React.FC = () => { const [userData, setUserData] = useState([]); // State to track user data const [nextId, setNextId] = useState(1); // Unique ID for new entries const { selectedFloorItem } = useSelectedFloorItem(); + const { objectPosition } = useObjectPosition(); + const { objectRotation } = useObjectRotation(); // Function to handle adding new user data const handleAddUserData = () => { const newUserData: UserData = { @@ -49,17 +50,19 @@ const AssetProperties: React.FC = () => { {/* Name */}
{selectedFloorItem.userData.name}
- {}} - value1={selectedFloorItem.position.x.toFixed(5)} - value2={selectedFloorItem.position.z.toFixed(5)} - /> - {}} - value={parseFloat( - THREE.MathUtils.radToDeg(selectedFloorItem.rotation.y).toFixed(5) - )} - /> + {objectPosition.x && objectPosition.z && + { }} + value1={parseFloat(objectPosition.x.toFixed(5))} + value2={parseFloat(objectPosition.z.toFixed(5))} + /> + } + {objectRotation.y && + { }} + value={parseFloat(objectRotation.y.toFixed(5))} + /> + }
diff --git a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx index 8881953..cf8ac60 100644 --- a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx @@ -16,7 +16,7 @@ import { useTileDistance, useToggleView, useWallVisibility, -} from "../../../../store/store"; +} from "../../../../store/builder/store"; import { setEnvironment } from "../../../../services/factoryBuilder/environment/setEnvironment"; import * as CONSTANTS from "../../../../types/world/worldConstants"; import { validateBBox } from "@turf/helpers"; diff --git a/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx b/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx index 0c79b3c..31c7761 100644 --- a/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/ZoneProperties.tsx @@ -7,7 +7,7 @@ import { usezonePosition, useZones, usezoneTarget, -} from "../../../../store/store"; +} from "../../../../store/builder/store"; import { zoneCameraUpdate } from "../../../../services/visulization/zone/zoneCameraUpdation"; const ZoneProperties: React.FC = () => { diff --git a/app/src/components/layout/sidebarRight/versionHisory/VersionHistory.tsx b/app/src/components/layout/sidebarRight/versionHisory/VersionHistory.tsx new file mode 100644 index 0000000..73a630c --- /dev/null +++ b/app/src/components/layout/sidebarRight/versionHisory/VersionHistory.tsx @@ -0,0 +1,128 @@ +import React, { useState } from "react"; +import { + AddIcon, + ArrowIcon, + CloseIcon, + KebabIcon, + LocationIcon, +} from "../../../icons/ExportCommonIcons"; +import RenameInput from "../../../ui/inputs/RenameInput"; + +const VersionHistory = () => { + const userName = localStorage.getItem("userName") ?? "Anonymous"; + + const initialVersions = [ + { + versionName: "v1.0", + timestamp: "April 09, 2025", + savedBy: userName, + }, + ]; + + const [versions, setVersions] = useState(initialVersions); + const [selectedVersion, setSelectedVersion] = useState(initialVersions[0]); + + const addNewVersion = () => { + const newVersionNumber = versions.length + 1; + const newVersion = { + versionName: `v${newVersionNumber}.0`, + timestamp: new Date().toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "2-digit", + }), + savedBy: userName, + }; + + const updated = [newVersion, ...versions]; + setVersions(updated); + setSelectedVersion(newVersion); + }; + + const handleSelectVersion = (version: any) => { + setSelectedVersion(version); + const reordered = [version, ...versions.filter((v) => v !== version)]; + setVersions(reordered); + }; + + const handleTimestampChange = (newTimestamp: string, index: number) => { + const updatedVersions = [...versions]; + updatedVersions[index].timestamp = newTimestamp; + setVersions(updatedVersions); + }; + + return ( +
+ {/* Header */} +
+
Version History
+
+ +
+ +
+
+ +
+
+
+ + {/* Shortcut Info */} +
+
i
+
+ Press Ctrl + Alt + S to add to version history while editing +
+
+ + {/* Current Version Display */} +
+
+ +
+
+
+ Current Version ({selectedVersion.versionName}) +
+
+ {versions.length} Saved History +
+
+
+ + {/* Versions List */} +
+ {versions.map((version, index) => ( + + ))} +
+
+ ); +}; + +export default VersionHistory; diff --git a/app/src/components/templates/CollaborationPopup.tsx b/app/src/components/templates/CollaborationPopup.tsx index c226b45..ec62fb5 100644 --- a/app/src/components/templates/CollaborationPopup.tsx +++ b/app/src/components/templates/CollaborationPopup.tsx @@ -5,7 +5,7 @@ import { AccessOption, User } from "../../types/users"; import RegularDropDown from "../ui/inputs/RegularDropDown"; import { access } from "fs"; import MultiEmailInvite from "../ui/inputs/MultiEmailInvite"; -import { useActiveUsers } from "../../store/store"; +import { useActiveUsers } from "../../store/builder/store"; interface UserListTemplateProps { user: User; diff --git a/app/src/components/templates/FollowPerson.tsx b/app/src/components/templates/FollowPerson.tsx index 832c7f1..63fb26a 100644 --- a/app/src/components/templates/FollowPerson.tsx +++ b/app/src/components/templates/FollowPerson.tsx @@ -1,7 +1,7 @@ import React from "react"; import RenderOverlay from "./Overlay"; import { useSelectedUserStore } from "../../store/useCollabStore"; -import { useCamMode } from "../../store/store"; +import { useCamMode } from "../../store/builder/store"; const FollowPerson: React.FC = () => { // Get the selected user from the store diff --git a/app/src/components/temporary/SelectFloorPlan.tsx b/app/src/components/temporary/SelectFloorPlan.tsx new file mode 100644 index 0000000..b206749 --- /dev/null +++ b/app/src/components/temporary/SelectFloorPlan.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import useLayoutStore from "../../store/builder/uselayoutStore"; + +const SelectFloorPlan: React.FC = () => { + const { currentLayout, setLayout } = useLayoutStore(); + return ( +
+ Don't have an idea? Use these presets! +
+ + +
+
+ ); +}; + +export default SelectFloorPlan; diff --git a/app/src/components/ui/ModuleToggle.tsx b/app/src/components/ui/ModuleToggle.tsx index 4a1532f..6ce5f33 100644 --- a/app/src/components/ui/ModuleToggle.tsx +++ b/app/src/components/ui/ModuleToggle.tsx @@ -7,10 +7,12 @@ import { VisualizationIcon, } from "../icons/ExportModuleIcons"; import useToggleStore from "../../store/useUIToggleStore"; +import useVersionHistoryStore from "../../store/builder/store"; const ModuleToggle: React.FC = () => { const { activeModule, setActiveModule } = useModuleStore(); const { setToggleUI } = useToggleStore(); + const { setVersionHistory } = useVersionHistoryStore(); return (
@@ -18,6 +20,7 @@ const ModuleToggle: React.FC = () => { className={`module-list ${activeModule === "builder" ? "active" : ""}`} onClick={() => { setActiveModule("builder"); + setVersionHistory(false); setToggleUI( localStorage.getItem("navBarUiLeft") ? localStorage.getItem("navBarUiLeft") === "true" @@ -39,6 +42,7 @@ const ModuleToggle: React.FC = () => { }`} onClick={() => { setActiveModule("simulation"); + setVersionHistory(false); setToggleUI( localStorage.getItem("navBarUiLeft") ? localStorage.getItem("navBarUiLeft") === "true" @@ -60,6 +64,7 @@ const ModuleToggle: React.FC = () => { }`} onClick={() => { setActiveModule("visualization"); + setVersionHistory(false); setToggleUI( localStorage.getItem("navBarUiLeft") ? localStorage.getItem("navBarUiLeft") === "true" @@ -79,6 +84,7 @@ const ModuleToggle: React.FC = () => { className={`module-list ${activeModule === "market" ? "active" : ""}`} onClick={() => { setActiveModule("market"); + setVersionHistory(false); setToggleUI(false, false); }} > diff --git a/app/src/components/ui/Tools.tsx b/app/src/components/ui/Tools.tsx index b9d98f4..5e7ee2f 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -29,9 +29,9 @@ import { useSocketStore, useToggleView, useToolMode, - useTransformMode, useActiveSubTool, -} from "../../store/store"; + useShortcutStore, +} from "../../store/builder/store"; import useToggleStore from "../../store/useUIToggleStore"; import { use3DWidget, @@ -50,6 +50,7 @@ const Tools: React.FC = () => { const { activeModule } = useModuleStore(); const { toggleThreeD, setToggleThreeD } = useThreeDStore(); const { isPlaying, setIsPlaying } = usePlayButtonStore(); + const { showShortcuts } = useShortcutStore(); const { activeTool, @@ -58,7 +59,6 @@ const Tools: React.FC = () => { setAddAction, setDeleteTool, setDeletePointOrLine, - setTransformMode, } = useStoreHooks(); const { setActiveSubTool, activeSubTool } = useActiveSubTool(); @@ -117,7 +117,6 @@ const Tools: React.FC = () => { setToolMode(null); setDeleteTool(false); setAddAction(null); - setTransformMode(null); setDeletePointOrLine(false); setRefTextUpdate((prev) => prev - 1); }; @@ -125,13 +124,9 @@ const Tools: React.FC = () => { const updateToolBehavior = (tool: string, is2D: boolean) => { switch (tool) { case "cursor": - is2D ? setToolMode("move") : setTransformMode("translate"); - break; - case "Rotate": - if (!is2D) setTransformMode("rotate"); - break; - case "Scale": - if (!is2D) setTransformMode("scale"); + if (toggleView) { + setToolMode('move'); + } break; case "draw-wall": is2D && setToolMode("Wall"); @@ -158,15 +153,21 @@ const Tools: React.FC = () => { }; const toggle2D3D = () => { - setToggleThreeD(!toggleThreeD); + const toggleTo2D = toggleView; + setToggleView(!toggleTo2D); + setToggleThreeD(toggleTo2D); + setToggleUI(toggleTo2D, toggleTo2D); + if (toggleTo2D) { + setSelectedWallItem(null); + setDeleteTool(false); + setAddAction(null); + } + setActiveTool("cursor"); + setActiveSubTool("cursor"); setToggleUI( localStorage.getItem("navBarUiLeft") !== "false", localStorage.getItem("navBarUiRight") !== "false" ); - setSelectedWallItem(null); - setActiveSubTool("cursor"); - setActiveTool("cursor"); - setToggleView(!toggleThreeD); }; if (isPlaying && activeModule !== "simulation") { @@ -300,7 +301,7 @@ const Tools: React.FC = () => { }; return ( -
+
{/* Tool Picker (cursor, delete, etc.) */} {["cursor", "free-hand", "delete"].map( @@ -388,7 +389,6 @@ const useStoreHooks = () => { ...useToolMode(), ...useDeleteTool(), ...useAddAction(), - ...useTransformMode(), ...useDeletePointOrLine(), ...useRefTextUpdate(), }; diff --git a/app/src/components/ui/list/DropDownList.tsx b/app/src/components/ui/list/DropDownList.tsx index 10ca425..5de5654 100644 --- a/app/src/components/ui/list/DropDownList.tsx +++ b/app/src/components/ui/list/DropDownList.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import List from "./List"; import { AddIcon, ArrowIcon, FocusIcon } from "../../icons/ExportCommonIcons"; import KebabMenuListMultiSelect from "./KebebMenuListMultiSelect"; -import { useFloorItems, useZones } from "../../../store/store"; +import { useFloorItems, useZones } from "../../../store/builder/store"; interface DropDownListProps { value?: string; // Value to display in the DropDownList diff --git a/app/src/components/ui/list/List.tsx b/app/src/components/ui/list/List.tsx index 80ecf8a..860bae1 100644 --- a/app/src/components/ui/list/List.tsx +++ b/app/src/components/ui/list/List.tsx @@ -12,7 +12,7 @@ import { LockIcon, RemoveIcon, } from "../../icons/ExportCommonIcons"; -import { useFloorItems, useZoneAssetId, useZones } from "../../../store/store"; +import { useFloorItems, useZoneAssetId, useZones } from "../../../store/builder/store"; import { zoneCameraUpdate } from "../../../services/visulization/zone/zoneCameraUpdation"; import { setFloorItemApi } from "../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; @@ -158,13 +158,13 @@ const List: React.FC = ({ items = [], remove }) => {
    {items?.map((item) => ( -
  • +
  • handleSelectZone(item.id)} + >
    - + ); + + const renderSubMenu = (submenu: MenuItem[], parentLabel: string) => ( +
    + {submenu.map((item) => ( + + ))} +
    + ); + + return ( +
    setOpenMenu(false)}>
    - {/* File Menu */} -
    setActiveMenu("File")} - onMouseLeave={() => { - setActiveMenu(null); - setActiveSubMenu(null); - }} - > -
    - File - - - -
    - - {/* File Dropdown */} - {activeMenu === "File" && ( -
    - {/* New File */} -
    toggleSelection("New File")} - > -
    - New File -
    - Ctrl + N -
    -
    -
    - - {/* Open Local File */} -
    toggleSelection("Open Local File")} - > -
    - Open Local File -
    - Ctrl + O -
    -
    -
    - - {/* Save Version */} -
    toggleSelection("Save Version")} - > -
    - Save Version -
    -
    -
    - - {/* Make a Copy */} -
    toggleSelection("Make a Copy")} - > -
    - Make a Copy -
    -
    - - {/* Share */} -
    toggleSelection("Share")} - > -
    - Share -
    -
    - - {/* Rename */} -
    toggleSelection("Rename")} - > -
    - Rename -
    -
    -
    - - {/* Import */} -
    toggleSelection("Import")} - > -
    - Import -
    -
    - - {/* Close File */} -
    toggleSelection("Close File")} - > -
    - Close File -
    -
    + {Object.entries(menus).map(([menu, items]) => ( +
    - {/* Edit Menu */} -
    setActiveMenu("Edit")} - onMouseLeave={() => { - setActiveMenu(null); - setActiveSubMenu(null); - }} - > -
    - Edit - - - -
    - - {/* Edit Dropdown */} - {activeMenu === "Edit" && ( -
    - {/* Undo */} -
    toggleSelection("Undo")} - > -
    - Undo -
    - Ctrl + Z -
    -
    -
    - - {/* Redo */} -
    toggleSelection("Redo")} - > -
    - Redo -
    - Ctrl + Shift + Z -
    -
    -
    -
    - - {/* Undo History */} -
    toggleSelection("Undo History")} - > -
    - Undo History -
    -
    - - {/* Redo History */} -
    toggleSelection("Redo History")} - > -
    - Redo History -
    -
    -
    - - {/* Find */} -
    toggleSelection("Find")} - > -
    - Find -
    - Ctrl + F -
    -
    -
    - - {/* Delete */} -
    toggleSelection("Delete")} - > -
    - Delete -
    -
    - - {/* Select by... */} -
    toggleSelection("Select by...")} - > -
    - Select by... -
    -
    - - {/* Keymap */} -
    toggleSelection("Keymap")} - > -
    - Keymap -
    -
    -
    - )} -
    - - {/* View Menu */} -
    setActiveMenu("View")} - onMouseLeave={() => { - setActiveMenu(null); - setActiveSubMenu(null); - }} - > -
    - View - - - -
    - - {/* View Dropdown */} - {activeMenu === "View" && ( -
    - {/* Grid */} -
    toggleSelection("Grid")} - > -
    - Grid -
    -
    - - {/* Gizmo */} -
    setActiveSubMenu("View-Gizmo")} - onMouseLeave={() => setActiveSubMenu(null)} - > -
    - Gizmo - - - -
    -
    - - {/* Gizmo Submenu */} - {activeSubMenu === "View-Gizmo" && ( -
    - {/* Visibility */} -
    toggleSelection("Visibility")} + {activeMenu === menu && ( +
    + {items.map((item) => + item.submenu ? ( +
    -
    - - {/* Cube view */} -
    toggleSelection("Cube view")} - > - Cube view -
    - - {/* Sphere view */} -
    toggleSelection("Sphere view")} - > - Sphere view -
    -
    +
    + {item.label} + + + +
    + {activeSubMenu === item.label && + renderSubMenu(item.submenu, item.label)} + + ) : ( + renderMenuItem(item) + ) )}
    + )} + + ))} - {/* Zoom */} -
    toggleSelection("Zoom")} - > -
    - Zoom -
    -
    - - {/* Full Screen */} -
    toggleSelection("Full Screen")} - > -
    - Full Screen -
    - F11 -
    -
    -
    -
    - )} -
    - - {/* Version History Menu */} -
    setActiveMenu("Version history")} onMouseLeave={() => { setActiveMenu(null); setActiveSubMenu(null); }} + onClick={() => { + setVersionHistory(true); + setSubModule("properties"); + }} >
    Version history
    -
    + - {/* Export As Menu */} -
    setActiveMenu("Export as...")} + onMouseEnter={() => setActiveMenu("Theme")} onMouseLeave={() => { setActiveMenu(null); setActiveSubMenu(null); }} - > -
    Export as...
    -
    - -
    setActiveMenu("theme")} - onMouseLeave={() => { - setActiveMenu(null); - setActiveSubMenu(null); - }} - onClick={() => { - handleThemeChange(); - }} + onClick={handleThemeChange} >
    Theme
    {savedTheme}
    -
    + - {/* Apps Menu */} - {/*
    setActiveMenu("Apps")} - onMouseLeave={() => { - setActiveMenu(null); - setActiveSubMenu(null); - }} - > -
    - Apps - - - -
    - - {activeMenu === "Apps" && ( -
    -
    toggleSelection("New App")} - > -
    - - - New App - -
    -
    -
    - -
    toggleSelection("Work-flow Monitor")} - > -
    - - - Work-flow Monitor - -
    -
    - -
    toggleSelection("Temperature Visualizer")} - > -
    - - - Temperature Visualizer - -
    -
    - -
    toggleSelection("View all")} - > -
    - - - View all - -
    -
    -
    - )} -
    */} - - {/* Help Menu */} -
    setActiveMenu("Help")} - onMouseLeave={() => { - setActiveMenu(null); - setActiveSubMenu(null); - }} - > -
    - Help - - - -
    - - {/* Help Dropdown */} - {activeMenu === "Help" && ( -
    - {/* Shortcuts */} -
    toggleSelection("Shortcuts")} - > -
    - Shortcuts -
    - Ctrl + Shift + ? -
    -
    -
    - - {/* Manual */} -
    toggleSelection("Manual")} - > -
    - Manual -
    -
    - - {/* Video Tutorials */} -
    toggleSelection("Video Tutorials")} - > -
    - Video Tutorials -
    -
    - - {/* Report a bug */} -
    toggleSelection("Report a bug")} - > -
    - Report a bug -
    -
    -
    - )} -
    -
    + {/* Log out */} +
    +
    ); diff --git a/app/src/components/ui/simulation/simulationPlayer.tsx b/app/src/components/ui/simulation/simulationPlayer.tsx index a2e05f9..0a5b2de 100644 --- a/app/src/components/ui/simulation/simulationPlayer.tsx +++ b/app/src/components/ui/simulation/simulationPlayer.tsx @@ -1,6 +1,6 @@ import React, { useState, useRef, useEffect } from "react"; import { ExitIcon, PlayStopIcon, ResetIcon } from "../../icons/SimulationIcons"; -import { useActiveTool } from "../../../store/store"; +import { useActiveTool } from "../../../store/builder/store"; import { useAnimationPlaySpeed, usePauseButtonStore, diff --git a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts index e625116..0d96c8d 100644 --- a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts @@ -71,7 +71,6 @@ async function loadInitialFloorItems( // Check Three.js Cache const cachedModel = THREE.Cache.get(item.modelfileID!); if (cachedModel) { - // processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems, addEvent); modelsLoaded++; checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); @@ -81,7 +80,6 @@ async function loadInitialFloorItems( // Check IndexedDB const indexedDBModel = await retrieveGLTF(item.modelfileID!); if (indexedDBModel) { - // const blobUrl = URL.createObjectURL(indexedDBModel); loader.load(blobUrl, (gltf) => { URL.revokeObjectURL(blobUrl); @@ -102,7 +100,6 @@ async function loadInitialFloorItems( } // Fetch from Backend - // const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`; loader.load(modelUrl, async (gltf) => { const modelBlob = await fetch(modelUrl).then((res) => res.blob()); @@ -338,7 +335,7 @@ function checkLoadingCompletion( resolve: () => void ) { if (modelsLoaded === modelsToLoad) { - toast.success("Models Loaded!"); + echo.success("Models Loaded!"); dracoLoader.dispose(); } resolve(); diff --git a/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts b/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts index f9799a8..2258f3b 100644 --- a/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialWallItems.ts @@ -1,54 +1,102 @@ -import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; - +import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; +import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; +import * as THREE from 'three'; import * as Types from "../../../types/world/worldTypes"; import { getWallItems } from '../../../services/factoryBuilder/assest/wallAsset/getWallItemsApi'; - -////////// Load the Wall Items's intially of there is any ////////// +import { retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils'; async function loadInitialWallItems( setWallItems: Types.setWallItemSetState, - AssetConfigurations: Types.AssetConfigurations ): Promise { + try { + const email = localStorage.getItem('email'); + if (!email) { + throw new Error('No email found in localStorage'); + } - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; + const organization = email.split("@")[1].split(".")[0]; + const items = await getWallItems(organization); - const items = await getWallItems(organization); + let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - localStorage.setItem("WallItems", JSON.stringify(items)); - if (items.length > 0) { - const storedWallItems: Types.wallItems = items; + if (!items || items.length === 0) { + localStorage.removeItem("WallItems"); + return; + } - const loadedWallItems = await Promise.all(storedWallItems.map(async (item) => { - const loader = new GLTFLoader(); + localStorage.setItem("WallItems", JSON.stringify(items)); + + const loader = new GLTFLoader(); + const dracoLoader = new DRACOLoader(); + dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); + loader.setDRACOLoader(dracoLoader); + + const loadedWallItems = await Promise.all(items.map(async (item: Types.WallItem) => { + // Check THREE.js cache first + const cachedModel = THREE.Cache.get(item.modelName!); + if (cachedModel) { + return processModel(cachedModel, item); + } + + // Check IndexedDB cache + const cachedModelBlob = await retrieveGLTF(item.modelfileID!); + if (cachedModelBlob) { + const blobUrl = URL.createObjectURL(cachedModelBlob); + return new Promise((resolve) => { + loader.load(blobUrl, (gltf) => { + URL.revokeObjectURL(blobUrl); + THREE.Cache.add(item.modelName!, gltf); + resolve(processModel(gltf, item)); + }); + }); + } + + // Load from original URL if not cached + const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`; return new Promise((resolve) => { - loader.load(AssetConfigurations[item.modelName!].modelUrl, (gltf) => { - const model = gltf.scene; - model.uuid = item.modelUuid!; - - model.children[0].children.forEach((child: any) => { - if (child.name !== "CSG_REF") { - child.castShadow = true; - child.receiveShadow = true; - } + loader.load(modelUrl, async (gltf) => { + try { + // Cache the model + const modelBlob = await fetch(modelUrl).then((res) => res.blob()); + await storeGLTF(item.modelName!, modelBlob); + THREE.Cache.add(item.modelName!, gltf); + resolve(processModel(gltf, item)); + } catch (error) { + console.error('Failed to cache model:', error); + resolve(processModel(gltf, item)); + } }); - - resolve({ - type: item.type, - model: model, - modelName: item.modelName, - scale: item.scale, - csgscale: item.csgscale, - csgposition: item.csgposition, - position: item.position, - quaternion: item.quaternion, - }); - }); }); })); setWallItems(loadedWallItems); + } catch (error) { + console.error('Failed to load wall items:', error); } } -export default loadInitialWallItems; +function processModel(gltf: GLTF, item: Types.WallItem): Types.WallItem { + const model = gltf.scene.clone(); + model.uuid = item.modelUuid!; + + model.children[0]?.children?.forEach((child: THREE.Object3D) => { + if (child.name !== "CSG_REF") { + child.castShadow = true; + child.receiveShadow = true; + } + }); + + return { + type: item.type, + model: model, + modelName: item.modelName, + modelfileID: item.modelfileID, + scale: item.scale, + csgscale: item.csgscale, + csgposition: item.csgposition, + position: item.position, + quaternion: item.quaternion, + }; +} + +export default loadInitialWallItems; \ No newline at end of file diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx index 4c8b1d7..ed82590 100644 --- a/app/src/modules/builder/builder.tsx +++ b/app/src/modules/builder/builder.tsx @@ -9,12 +9,6 @@ import { useThree, useFrame } from "@react-three/fiber"; import DistanceText from "./geomentries/lines/distanceText/distanceText"; import ReferenceDistanceText from "./geomentries/lines/distanceText/referenceDistanceText"; -////////// Assests Imports ////////// - -// import arch from "../../assets/gltf-glb/arch.glb"; -// import door from "../../assets/gltf-glb/door.glb"; -// import Window from "../../assets/gltf-glb/window.glb"; - ////////// Zustand State Imports ////////// import { @@ -30,7 +24,7 @@ import { useRefTextUpdate, useRenderDistance, useLimitDistance, -} from "../../store/store"; +} from "../../store/builder/store"; ////////// 3D Function Imports ////////// @@ -52,59 +46,60 @@ 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"; export default function Builder() { - const state = useThree(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements. - const csg = useRef(); // Reference for CSG object, used for 3D modeling. - const CSGGroup = useRef() as Types.RefMesh; // Reference to a group of CSG objects. - const scene = useRef() as Types.RefScene; // Reference to the scene. - const camera = useRef() as Types.RefCamera; // Reference to the camera object. - const controls = useRef(); // Reference to the controls object. - const raycaster = useRef() as Types.RefRaycaster; // Reference for raycaster used for detecting objects being pointed at in the scene. - const dragPointControls = useRef() as Types.RefDragControl; // Reference for drag point controls, an array for drag control. + const state = useThree(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements. + const csg = useRef(); // Reference for CSG object, used for 3D modeling. + const CSGGroup = useRef() as Types.RefMesh; // Reference to a group of CSG objects. + const scene = useRef() as Types.RefScene; // Reference to the scene. + const camera = useRef() as Types.RefCamera; // Reference to the camera object. + const controls = useRef(); // Reference to the controls object. + const raycaster = useRef() as Types.RefRaycaster; // Reference for raycaster used for detecting objects being pointed at in the scene. + const dragPointControls = useRef() as Types.RefDragControl; // Reference for drag point controls, an array for drag control. - // Assigning the scene and camera from the Three.js state to the references. + // Assigning the scene and camera from the Three.js state to the references. - scene.current = state.scene; - camera.current = state.camera; - controls.current = state.controls; - raycaster.current = state.raycaster; + scene.current = state.scene; + camera.current = state.camera; + controls.current = state.controls; + raycaster.current = state.raycaster; - const plane = useRef(null); // Reference for a plane object for raycaster reference. - const grid = useRef() as any; // Reference for a grid object for raycaster reference. - const snappedPoint = useRef() as Types.RefVector3; // Reference for storing a snapped point at the (end = isSnapped) and (start = ispreSnapped) of the line. - const isSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (end). - const anglesnappedPoint = useRef() as Types.RefVector3; // Reference for storing an angle-snapped point when the line is in 90 degree etc... - const isAngleSnapped = useRef(false) as Types.RefBoolean; // Boolean to indicate if angle snapping is active. - const isSnappedUUID = useRef() as Types.RefString; // UUID reference to identify the snapped point. - const ispreSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (start). - const tempLoader = useRef() as Types.RefMesh; // Reference for a temporary loader for the floor items. - const isTempLoader = useRef() as Types.RefBoolean; // Reference to check if a temporary loader is active. - const Tube = useRef() as Types.RefTubeGeometry; // Reference for tubes used for reference line creation and updation. - const line = useRef([]) as Types.RefLine; // Reference for line which stores the current line that is being drawn. - const lines = useRef([]) as Types.RefLines; // Reference for lines which stores all the lines that are ever drawn. - const onlyFloorline = useRef([]); // Reference for floor lines which does not have walls or roof and have only floor used to store the current line that is being drawn. - const onlyFloorlines = useRef([]); // Reference for all the floor lines that are ever drawn. - const ReferenceLineMesh = useRef() as Types.RefMesh; // Reference for storing the mesh of the reference line for moving it during draw. - const LineCreated = useRef(false) as Types.RefBoolean; // Boolean to track whether the reference line is created or not. - const referencePole = useRef() as Types.RefMesh; // Reference for a pole that is used as the reference for the user to show where it is placed. - const itemsGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the floor items (Gltf). - const floorGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the roofs and the floors. - const AttachedObject = useRef() as Types.RefMesh; // Reference for an object that is attached using dbl click for transform controls rotation. - const floorPlanGroup = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines group and the points group. - const floorPlanGroupLine = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines that are drawn. - const floorPlanGroupPoint = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the points that are created. - const floorGroupAisle = useRef() as Types.RefGroup; - const zoneGroup = useRef() as Types.RefGroup; - const currentLayerPoint = useRef([]) as Types.RefMeshArray; // Reference for points that re in the current layer used to update the points in drag controls. - const hoveredDeletablePoint = useRef() as Types.RefMesh; // Reference for the currently hovered point that can be deleted. - const hoveredDeletableLine = useRef() as Types.RefMesh; // Reference for the currently hovered line that can be deleted. - const hoveredDeletableFloorItem = useRef() as Types.RefMesh; // Reference for the currently hovered floor item that can be deleted. - const hoveredDeletableWallItem = useRef() as Types.RefMesh; // Reference for the currently hovered wall item that can be deleted. - const hoveredDeletablePillar = useRef() as Types.RefMesh; // Reference for the currently hovered pillar that can be deleted. - const currentWallItem = useRef() as Types.RefMesh; // Reference for the currently selected wall item that can be scaled, dragged etc... + const plane = useRef(null); // Reference for a plane object for raycaster reference. + const grid = useRef() as any; // Reference for a grid object for raycaster reference. + const snappedPoint = useRef() as Types.RefVector3; // Reference for storing a snapped point at the (end = isSnapped) and (start = ispreSnapped) of the line. + const isSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (end). + const anglesnappedPoint = useRef() as Types.RefVector3; // Reference for storing an angle-snapped point when the line is in 90 degree etc... + const isAngleSnapped = useRef(false) as Types.RefBoolean; // Boolean to indicate if angle snapping is active. + const isSnappedUUID = useRef() as Types.RefString; // UUID reference to identify the snapped point. + const ispreSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (start). + const tempLoader = useRef() as Types.RefMesh; // Reference for a temporary loader for the floor items. + const isTempLoader = useRef() as Types.RefBoolean; // Reference to check if a temporary loader is active. + const Tube = useRef() as Types.RefTubeGeometry; // Reference for tubes used for reference line creation and updation. + const line = useRef([]) as Types.RefLine; // Reference for line which stores the current line that is being drawn. + const lines = useRef([]) as Types.RefLines; // Reference for lines which stores all the lines that are ever drawn. + const onlyFloorline = useRef([]); // Reference for floor lines which does not have walls or roof and have only floor used to store the current line that is being drawn. + const onlyFloorlines = useRef([]); // Reference for all the floor lines that are ever drawn. + const ReferenceLineMesh = useRef() as Types.RefMesh; // Reference for storing the mesh of the reference line for moving it during draw. + const LineCreated = useRef(false) as Types.RefBoolean; // Boolean to track whether the reference line is created or not. + const referencePole = useRef() as Types.RefMesh; // Reference for a pole that is used as the reference for the user to show where it is placed. + const itemsGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the floor items (Gltf). + const floorGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the roofs and the floors. + const AttachedObject = useRef() as Types.RefMesh; // Reference for an object that is attached using dbl click for transform controls rotation. + const floorPlanGroup = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines group and the points group. + const floorPlanGroupLine = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines that are drawn. + const floorPlanGroupPoint = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the points that are created. + const floorGroupAisle = useRef() as Types.RefGroup; + const zoneGroup = useRef() as Types.RefGroup; + const currentLayerPoint = useRef([]) as Types.RefMeshArray; // Reference for points that re in the current layer used to update the points in drag controls. + const hoveredDeletablePoint = useRef() as Types.RefMesh; // Reference for the currently hovered point that can be deleted. + const hoveredDeletableLine = useRef() as Types.RefMesh; // Reference for the currently hovered line that can be deleted. + const hoveredDeletableFloorItem = useRef() as Types.RefMesh; // Reference for the currently hovered floor item that can be deleted. + const hoveredDeletableWallItem = useRef() as Types.RefMesh; // Reference for the currently hovered wall item that can be deleted. + const hoveredDeletablePillar = useRef() as Types.RefMesh; // Reference for the currently hovered pillar that can be deleted. + const currentWallItem = useRef() as Types.RefMesh; // Reference for the currently selected wall item that can be scaled, dragged etc... - const cursorPosition = new THREE.Vector3(); // 3D vector for storing the cursor position. + const 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. @@ -120,42 +115,13 @@ export default function Builder() { const { setWalls } = useWalls(); const { refTextupdate, setRefTextUpdate } = useRefTextUpdate(); - // const loader = new GLTFLoader(); - // const dracoLoader = new 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); + // dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); + // loader.setDRACOLoader(dracoLoader); - ////////// Assest Configuration Values ////////// - - const AssetConfigurations: Types.AssetConfigurations = { - // arch: { - // modelUrl: arch, - // scale: [0.75, 0.75, 0.75], - // csgscale: [2, 4, 0.5], - // csgposition: [0, 2, 0], - // positionY: () => 0, - // type: "Fixed-Move", - // }, - // door: { - // modelUrl: door, - // scale: [0.75, 0.75, 0.75], - // csgscale: [2, 4, 0.5], - // csgposition: [0, 2, 0], - // positionY: () => 0, - // type: "Fixed-Move", - // }, - // window: { - // modelUrl: Window, - // scale: [0.75, 0.75, 0.75], - // csgscale: [5, 3, 0.5], - // csgposition: [0, 1.5, 0], - // positionY: (intersectionPoint) => intersectionPoint.point.y, - // type: "Free-Move", - // }, - }; - - ////////// All Toggle's ////////// + ////////// All Toggle's ////////// useEffect(() => { setRefTextUpdate((prevUpdate: number) => prevUpdate - 1); @@ -177,167 +143,169 @@ export default function Builder() { } }, [toggleView]); - useEffect(() => { - THREE.Cache.clear(); - THREE.Cache.enabled = true; - }, []); + useEffect(() => { + THREE.Cache.clear(); + THREE.Cache.enabled = true; + }, []); - useEffect(() => { - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; + useEffect(() => { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; - async function fetchVisibility() { - const visibility = await findEnvironment( - organization, - localStorage.getItem("userId")! - ); - if (visibility) { - setRoofVisibility(visibility.roofVisibility); - setWallVisibility(visibility.wallVisibility); - setShadows(visibility.shadowVisibility); - setRenderDistance(visibility.renderDistance); - setLimitDistance(visibility.limitDistance); - } - } - fetchVisibility(); - }, []); + async function fetchVisibility() { + const visibility = await findEnvironment( + organization, + localStorage.getItem("userId")! + ); + if (visibility) { + setRoofVisibility(visibility.roofVisibility); + setWallVisibility(visibility.wallVisibility); + setShadows(visibility.shadowVisibility); + setRenderDistance(visibility.renderDistance); + setLimitDistance(visibility.limitDistance); + } + } + fetchVisibility(); + }, []); - ////////// UseFrame is Here ////////// + ////////// UseFrame is Here ////////// - useFrame(() => { - if (toolMode) { - Draw( - state, - plane, - cursorPosition, - floorPlanGroupPoint, - floorPlanGroupLine, - snappedPoint, - isSnapped, - isSnappedUUID, - line, - lines, - ispreSnapped, - floorPlanGroup, - ReferenceLineMesh, - LineCreated, - setRefTextUpdate, - Tube, - anglesnappedPoint, - isAngleSnapped, - toolMode - ); - } - }); + useFrame(() => { + if (toolMode) { + Draw( + state, + plane, + cursorPosition, + floorPlanGroupPoint, + floorPlanGroupLine, + snappedPoint, + isSnapped, + isSnappedUUID, + line, + lines, + ispreSnapped, + floorPlanGroup, + ReferenceLineMesh, + LineCreated, + setRefTextUpdate, + Tube, + anglesnappedPoint, + isAngleSnapped, + toolMode + ); + } + }); - ////////// Return ////////// + ////////// Return ////////// - return ( - <> - + return ( + <> + - + - + - + - + - + - + - + - + - + - - - - - ); + + + + + + + + + ); } diff --git a/app/src/modules/builder/csg/csg.tsx b/app/src/modules/builder/csg/csg.tsx index 7e49598..bec85cd 100644 --- a/app/src/modules/builder/csg/csg.tsx +++ b/app/src/modules/builder/csg/csg.tsx @@ -1,54 +1,63 @@ import * as THREE from "three"; import { Geometry, Base, Subtraction } from "@react-three/csg"; -import { useDeleteTool } from "../../../store/store"; +import { useDeleteTool } from "../../../store/builder/store"; import { useRef } from "react"; export interface CsgProps { - position: THREE.Vector3 | [number, number, number]; - scale: THREE.Vector3 | [number, number, number]; - model: THREE.Object3D; - hoveredDeletableWallItem: { current: THREE.Mesh | null }; + position: THREE.Vector3 | [number, number, number]; + scale: THREE.Vector3 | [number, number, number]; + model: THREE.Object3D; + hoveredDeletableWallItem: { current: THREE.Mesh | null }; } export const Csg: React.FC = (props) => { - const { deleteTool } = useDeleteTool(); - const modelRef = useRef(); - const originalMaterials = useRef>(new Map()); + const { deleteTool } = useDeleteTool(); + const modelRef = useRef(); + const originalMaterials = useRef>(new Map()); - const handleHover = (hovered: boolean, object: THREE.Mesh | null) => { - if (modelRef.current && deleteTool) { - modelRef.current.traverse((child) => { - if (child instanceof THREE.Mesh) { - if (!originalMaterials.current.has(child)) { - originalMaterials.current.set(child, child.material); - } - child.material = child.material.clone(); - child.material.color.set(hovered && deleteTool ? 0xff0000 : (originalMaterials.current.get(child) as any).color); + const handleHover = (hovered: boolean, object: THREE.Mesh | null) => { + if (modelRef.current && deleteTool) { + modelRef.current.traverse((child) => { + if (child instanceof THREE.Mesh) { + if (!originalMaterials.current.has(child)) { + originalMaterials.current.set(child, child.material); + } + child.material = child.material.clone(); + child.material.color.set(hovered && deleteTool ? 0xff0000 : (originalMaterials.current.get(child) as any).color); + } + }); } - }); - } - props.hoveredDeletableWallItem.current = hovered ? object : null; - }; + let currentObject = object; + while (currentObject) { + if (currentObject.name === "Scene") { + break; + } + currentObject = currentObject.parent as THREE.Mesh; + } + if (currentObject) { + props.hoveredDeletableWallItem.current = hovered ? currentObject : null; + } + }; - return ( - - + return ( - + + + + + + { + e.stopPropagation(); + handleHover(true, e.object.parent); + }} + onPointerOut={(e: any) => { + e.stopPropagation(); + handleHover(false, null); + }} + /> - - { - e.stopPropagation(); - handleHover(true, e.object.parent); - }} - onPointerOut={(e: any) => { - e.stopPropagation(); - handleHover(false, null); - }} - /> - - ); + ); }; diff --git a/app/src/modules/builder/eventFunctions/handleMeshDown.ts b/app/src/modules/builder/eventFunctions/handleMeshDown.ts index 1f00038..9fb7839 100644 --- a/app/src/modules/builder/eventFunctions/handleMeshDown.ts +++ b/app/src/modules/builder/eventFunctions/handleMeshDown.ts @@ -35,29 +35,26 @@ function handleMeshDown( } if (event.intersections.length > 0) { - const clickedIndex = wallItems.findIndex((item) => item.model === event.intersections[0]?.object?.parent?.parent); - if (clickedIndex !== -1) { - setSelectedItemsIndex(clickedIndex); - const wallItemModel = wallItems[clickedIndex]?.model; - if (wallItemModel && wallItemModel.parent && wallItemModel.parent.parent) { - currentWallItem.current = (wallItemModel.parent.parent.children[0]?.children[1]?.children[0] as Types.Mesh) || null; - setSelectedWallItem(wallItemModel.parent); - // currentWallItem.current?.children.forEach((child) => { - // if ((child as THREE.Mesh).isMesh && child.name !== "CSG_REF") { - // const material = (child as THREE.Mesh).material; - // if (Array.isArray(material)) { - // material.forEach(mat => { - // if (mat instanceof THREE.MeshStandardMaterial) { - // mat.emissive = new THREE.Color("green"); - // } - // }); - // } else if (material instanceof THREE.MeshStandardMaterial) { - // material.emissive = new THREE.Color("green"); - // } - // } - // }); + if (event.object) { + const wallItemModel = event.object; + let currentObject = wallItemModel as THREE.Object3D; + while (currentObject) { + if (currentObject.name === "Scene") { + break; + } + currentObject = currentObject.parent as THREE.Object3D; + } + if (!currentObject) return; + const clickedIndex = wallItems.findIndex((item) => item.model?.uuid === currentObject.uuid); + if (clickedIndex !== -1) { + setSelectedItemsIndex(clickedIndex); + const wallItemModel = wallItems[clickedIndex]?.model; + if (wallItemModel) { + setSelectedWallItem(wallItemModel); + } } } + } } } diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index 6023422..692664e 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -397,7 +397,7 @@ async function handleModelLoad( socket.emit("v2:model-asset:add", completeData); gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" }); - gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { toast.success("Model Added!"); } }); + gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { echo.success("Model Added!"); } }); } else { const data = { @@ -421,7 +421,7 @@ async function handleModelLoad( socket.emit("v2:model-asset:add", data); gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" }); - gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { toast.success("Model Added!"); } }); + gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { echo.success("Model Added!"); } }); } } diff --git a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts b/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts index f13ebee..5b234b4 100644 --- a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts +++ b/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts @@ -80,7 +80,7 @@ async function DeleteFloorItems( } setFloorItems(updatedItems); - toast.success("Model Removed!"); + echo.success("Model Removed!"); } } } diff --git a/app/src/modules/builder/geomentries/floors/addFloorToScene.ts b/app/src/modules/builder/geomentries/floors/addFloorToScene.ts index 7ee2ceb..71f2d24 100644 --- a/app/src/modules/builder/geomentries/floors/addFloorToScene.ts +++ b/app/src/modules/builder/geomentries/floors/addFloorToScene.ts @@ -26,7 +26,6 @@ export default function addFloorToScene( if (materialCache.has(materialKey)) { material = materialCache.get(materialKey) as THREE.Material; - // } else { } else { const floorTexture = textureLoader.load(savedTheme === "dark" ? texturePathDark : texturePath); // const floorTexture = textureLoader.load(texturePath); diff --git a/app/src/modules/builder/geomentries/layers/deleteLayer.ts b/app/src/modules/builder/geomentries/layers/deleteLayer.ts index 41afa6e..4fe2a28 100644 --- a/app/src/modules/builder/geomentries/layers/deleteLayer.ts +++ b/app/src/modules/builder/geomentries/layers/deleteLayer.ts @@ -83,7 +83,7 @@ async function DeleteLayer( floorGroup.current?.remove(meshToRemove); } - toast.success("Layer Removed!"); + echo.success("Layer Removed!"); setRemovedLayer(null); } export default DeleteLayer; diff --git a/app/src/modules/builder/geomentries/lines/deleteLine.ts b/app/src/modules/builder/geomentries/lines/deleteLine.ts index 14a5e27..42b32b3 100644 --- a/app/src/modules/builder/geomentries/lines/deleteLine.ts +++ b/app/src/modules/builder/geomentries/lines/deleteLine.ts @@ -82,7 +82,7 @@ function deleteLine( } }); - toast.success("Line Removed!"); + echo.success("Line Removed!"); } export default deleteLine; diff --git a/app/src/modules/builder/geomentries/lines/distanceText/distanceText.tsx b/app/src/modules/builder/geomentries/lines/distanceText/distanceText.tsx index 40a68a3..58302f7 100644 --- a/app/src/modules/builder/geomentries/lines/distanceText/distanceText.tsx +++ b/app/src/modules/builder/geomentries/lines/distanceText/distanceText.tsx @@ -7,7 +7,7 @@ import { useNewLines, useRoomsState, useToggleView, -} from "../../../../../store/store"; +} from "../../../../../store/builder/store"; import objectLinesToArray from "../lineConvertions/objectLinesToArray"; import { Html } from "@react-three/drei"; import { Vector2 } from "three"; diff --git a/app/src/modules/builder/geomentries/lines/distanceText/referenceDistanceText.tsx b/app/src/modules/builder/geomentries/lines/distanceText/referenceDistanceText.tsx index 7342b1e..ce9864d 100644 --- a/app/src/modules/builder/geomentries/lines/distanceText/referenceDistanceText.tsx +++ b/app/src/modules/builder/geomentries/lines/distanceText/referenceDistanceText.tsx @@ -1,7 +1,7 @@ import * as THREE from "three"; import { Html } from "@react-three/drei"; import { useState, useEffect } from "react"; -import { useActiveLayer } from "../../../../../store/store"; +import { useActiveLayer } from "../../../../../store/builder/store"; const ReferenceDistanceText = ({ line }: { line: any }) => { interface TextState { diff --git a/app/src/modules/builder/geomentries/pillars/deletePillar.ts b/app/src/modules/builder/geomentries/pillars/deletePillar.ts index b735c81..39e0b28 100644 --- a/app/src/modules/builder/geomentries/pillars/deletePillar.ts +++ b/app/src/modules/builder/geomentries/pillars/deletePillar.ts @@ -13,7 +13,7 @@ function DeletePillar( (hoveredDeletablePillar.current.material).dispose(); (hoveredDeletablePillar.current.geometry).dispose(); floorGroup.current.remove(hoveredDeletablePillar.current); - toast.success("Pillar Removed!"); + echo.success("Pillar Removed!"); hoveredDeletablePillar.current = undefined; } } diff --git a/app/src/modules/builder/geomentries/points/deletePoint.ts b/app/src/modules/builder/geomentries/points/deletePoint.ts index 937e57e..827818f 100644 --- a/app/src/modules/builder/geomentries/points/deletePoint.ts +++ b/app/src/modules/builder/geomentries/points/deletePoint.ts @@ -51,7 +51,7 @@ function deletePoint( RemoveConnectedLines(DeletedPointUUID, floorPlanGroupLine, floorPlanGroupPoint, setDeletedLines, lines); - toast.success("Point Removed!"); + echo.success("Point Removed!"); } export default deletePoint; diff --git a/app/src/modules/builder/geomentries/walls/addWallItems.ts b/app/src/modules/builder/geomentries/walls/addWallItems.ts index fd9eb48..415fd35 100644 --- a/app/src/modules/builder/geomentries/walls/addWallItems.ts +++ b/app/src/modules/builder/geomentries/walls/addWallItems.ts @@ -1,108 +1,138 @@ -import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; +import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { toast } from 'react-toastify'; - import * as THREE from 'three'; import * as Types from "../../../../types/world/worldTypes"; import * as CONSTANTS from '../../../../types/world/worldConstants'; -// import { setWallItem } from '../../../../services/factoryBuilder/assest/wallAsset/setWallItemApi'; import { Socket } from 'socket.io-client'; +import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils'; +import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; async function AddWallItems( - selected: Types.String, + selected: any, raycaster: THREE.Raycaster, CSGGroup: Types.RefMesh, - AssetConfigurations: Types.AssetConfigurations, setWallItems: Types.setWallItemSetState, socket: Socket ): Promise { - - ////////// Load Wall GLtf's and set the positions, rotation, type etc. in state and store in localstorage ////////// - let intersects = raycaster?.intersectObject(CSGGroup.current!, true); const wallRaycastIntersection = intersects?.find((child) => child.object.name.includes("WallRaycastReference")); + let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - if (wallRaycastIntersection) { - const intersectionPoint = wallRaycastIntersection; - const loader = new GLTFLoader(); - loader.load(AssetConfigurations[selected].modelUrl, async (gltf) => { - const model = gltf.scene; - model.userData = { wall: intersectionPoint.object.parent }; - model.children[0].children.forEach((child) => { - if (child.name !== "CSG_REF") { - child.castShadow = true; - child.receiveShadow = true; - } + if (!wallRaycastIntersection) return; + + const intersectionPoint = wallRaycastIntersection; + const loader = new GLTFLoader(); + const dracoLoader = new DRACOLoader(); + + dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); + loader.setDRACOLoader(dracoLoader); + + // Check THREE.js cache first + const cachedModel = THREE.Cache.get(selected.id); + if (cachedModel) { + handleModelLoad(cachedModel); + return; + } + + // Check IndexedDB cache + const cachedModelBlob = await retrieveGLTF(selected.id); + if (cachedModelBlob) { + const blobUrl = URL.createObjectURL(cachedModelBlob); + loader.load(blobUrl, (gltf) => { + URL.revokeObjectURL(blobUrl); + THREE.Cache.remove(blobUrl); + THREE.Cache.add(selected.id, gltf); + handleModelLoad(gltf); + }); + return; + } + + // Load from backend if not in any cache + loader.load(`${url_Backend_dwinzo}/api/v2/AssetFile/${selected.id}`, async (gltf) => { + try { + const modelBlob = await fetch(`${url_Backend_dwinzo}/api/v2/AssetFile/${selected.id}`).then((res) => res.blob()); + await storeGLTF(selected.id, modelBlob); + THREE.Cache.add(selected.id, gltf); + await handleModelLoad(gltf); + } catch (error) { + console.error('Failed to cache model:', error); + handleModelLoad(gltf); + } + }); + + async function handleModelLoad(gltf: GLTF) { + const model = gltf.scene.clone(); + model.userData = { wall: intersectionPoint.object.parent }; + + model.children[0].children.forEach((child) => { + if (child.name !== "CSG_REF") { + child.castShadow = true; + child.receiveShadow = true; + } + }); + + const boundingBox = new THREE.Box3().setFromObject(model); + const size = new THREE.Vector3(); + boundingBox.getSize(size); + + const csgscale = [size.x, size.y, size.z] as [number, number, number]; + + const center = new THREE.Vector3(); + boundingBox.getCenter(center); + const csgposition = [center.x, center.y, center.z] as [number, number, number]; + + let positionY = selected.subCategory === 'fixed-move' ? 0 : intersectionPoint.point.y; + if (positionY === 0) { + positionY = Math.floor(intersectionPoint.point.y / CONSTANTS.wallConfig.height) * CONSTANTS.wallConfig.height; + } + + const newWallItem = { + type: selected.subCategory, + model: model, + modelName: selected.name, + modelfileID: selected.id, + scale: [1, 1, 1] as [number, number, number], + csgscale: csgscale, + csgposition: csgposition, + position: [intersectionPoint.point.x, positionY, intersectionPoint.point.z] as [number, number, number], + quaternion: intersectionPoint.object.quaternion.clone() as Types.QuaternionType + }; + + const email = localStorage.getItem('email'); + const organization = email ? (email.split("@")[1]).split(".")[0] : 'default'; + + const data = { + organization: organization, + modelUuid: model.uuid, + modelName: newWallItem.modelName, + modelfileID: selected.id, + type: selected.subCategory, + csgposition: newWallItem.csgposition, + csgscale: newWallItem.csgscale, + position: newWallItem.position, + quaternion: newWallItem.quaternion, + scale: newWallItem.scale, + socketId: socket.id + }; + + socket.emit('v1:wallItems:set', data); + + setWallItems((prevItems) => { + const updatedItems = [...prevItems, newWallItem]; + + const WallItemsForStorage = updatedItems.map(item => { + const { model, ...rest } = item; + return { + ...rest, + modelUuid: model?.uuid, + }; }); - const config = AssetConfigurations[selected]; - let positionY = typeof config.positionY === 'function' ? config.positionY(intersectionPoint) : config.positionY; - if (positionY === 0) { - positionY = Math.floor(intersectionPoint.point.y / CONSTANTS.wallConfig.height) * CONSTANTS.wallConfig.height; - } - - const newWallItem = { - type: config.type, - model: model, - modelName: selected, - scale: config.scale, - csgscale: config.csgscale, - csgposition: config.csgposition, - position: [intersectionPoint.point.x, positionY, intersectionPoint.point.z] as [number, number, number], - quaternion: intersectionPoint.object.quaternion.clone() as Types.QuaternionType - }; - - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; - - //REST - - // await setWallItem( - // organization, - // model.uuid, - // newWallItem.modelName, - // newWallItem.type!, - // newWallItem.csgposition!, - // newWallItem.csgscale!, - // newWallItem.position, - // newWallItem.quaternion, - // newWallItem.scale!, - // ) - - //SOCKET - - const data = { - organization: organization, - modelUuid: model.uuid, - modelName: newWallItem.modelName, - type: newWallItem.type!, - csgposition: newWallItem.csgposition!, - csgscale: newWallItem.csgscale!, - position: newWallItem.position, - quaternion: newWallItem.quaternion, - scale: newWallItem.scale!, - socketId: socket.id - } - - socket.emit('v1:wallItems:set', data); - - setWallItems((prevItems) => { - const updatedItems = [...prevItems, newWallItem]; - - const WallItemsForStorage = updatedItems.map(item => { - const { model, ...rest } = item; - return { - ...rest, - modelUuid: model?.uuid, - }; - }); - - localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); - toast.success("Model Added!"); - - return updatedItems; - }); + localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); + echo.success("Model Added!"); + return updatedItems; }); } } -export default AddWallItems; +export default AddWallItems; \ No newline at end of file diff --git a/app/src/modules/builder/geomentries/walls/deleteWallItems.ts b/app/src/modules/builder/geomentries/walls/deleteWallItems.ts index b5d40f4..80bc75f 100644 --- a/app/src/modules/builder/geomentries/walls/deleteWallItems.ts +++ b/app/src/modules/builder/geomentries/walls/deleteWallItems.ts @@ -1,5 +1,3 @@ -import { toast } from 'react-toastify'; - import * as Types from "../../../../types/world/worldTypes"; // import { deleteWallItem } from '../../../../services/factoryBuilder/assest/wallAsset/deleteWallItemApi'; import { Socket } from 'socket.io-client'; @@ -13,11 +11,11 @@ function DeleteWallItems( ////////// Deleting the hovered Wall GLTF from thewallItems and also update it in the localstorage ////////// - if (hoveredDeletableWallItem.current && hoveredDeletableWallItem.current.parent) { + if (hoveredDeletableWallItem.current && hoveredDeletableWallItem.current) { setWallItems([]); let WallItemsRef = wallItems; - const removedItem = WallItemsRef.find((item) => item.model?.uuid === hoveredDeletableWallItem.current?.parent?.uuid); - const Items = WallItemsRef.filter((item) => item.model?.uuid !== hoveredDeletableWallItem.current?.parent?.uuid); + const removedItem = WallItemsRef.find((item) => item.model?.uuid === hoveredDeletableWallItem.current?.uuid); + const Items = WallItemsRef.filter((item) => item.model?.uuid !== hoveredDeletableWallItem.current?.uuid); setTimeout(async () => { WallItemsRef = Items; @@ -50,7 +48,6 @@ function DeleteWallItems( }); localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); - toast.success("Model Removed!"); hoveredDeletableWallItem.current = null; }, 50); } diff --git a/app/src/modules/builder/groups/calculateAreaGroup.tsx b/app/src/modules/builder/groups/calculateAreaGroup.tsx index e50ae4b..f6a5057 100644 --- a/app/src/modules/builder/groups/calculateAreaGroup.tsx +++ b/app/src/modules/builder/groups/calculateAreaGroup.tsx @@ -1,65 +1,114 @@ -import React, { useEffect } from 'react'; -import { useRoomsState, useToggleView } from '../../../store/store'; -import { computeArea } from '../functions/computeArea'; -import { Html } from '@react-three/drei'; +import { useRoomsState, useToggleView } from "../../../store/builder/store"; +import { computeArea } from "../functions/computeArea"; +import { Html } from "@react-three/drei"; import * as CONSTANTS from "../../../types/world/worldConstants"; -import * as turf from '@turf/turf'; +import * as turf from "@turf/turf"; +import * as THREE from "three"; const CalculateAreaGroup = () => { - const { roomsState } = useRoomsState(); - const { toggleView } = useToggleView(); + const { roomsState } = useRoomsState(); + const { toggleView } = useToggleView(); + const savedTheme: string | null = localStorage.getItem("theme"); - return ( - - {roomsState.length > 0 && - roomsState.flat().map((room: any, index: number) => { - if (!toggleView) return null; - const coordinates = room.coordinates; + return ( + + + {roomsState.length > 0 && + roomsState.flat().map((room: any, index: number) => { + const coordinates = room.coordinates; + if (!coordinates || coordinates.length < 3) return null; - if (!coordinates || coordinates.length < 3) return null; + const coords2D = coordinates.map( + (p: any) => new THREE.Vector2(p.position.x, p.position.z) + ); - let coords2D = coordinates.map((p: any) => [p.position.x, p.position.z]); + if (!coords2D[0].equals(coords2D[coords2D.length - 1])) { + coords2D.push(coords2D[0]); + } - const first = coords2D[0]; - const last = coords2D[coords2D.length - 1]; - if (first[0] !== last[0] || first[1] !== last[1]) { - coords2D.push(first); - } + const shape = new THREE.Shape(coords2D); + const extrudeSettings = { + depth: 0.01, + bevelEnabled: false, + }; - const polygon = turf.polygon([coords2D]); - const center2D = turf.center(polygon).geometry.coordinates; + const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); + geometry.rotateX(Math.PI / 2); - const sumY = coordinates.reduce((sum: number, p: any) => sum + p.position.y, 0); - const avgY = sumY / coordinates.length; + const material = new THREE.MeshBasicMaterial({ + color: savedTheme === "dark" ? "#d2baff" : "#6f42c1", + side: THREE.DoubleSide, + transparent: true, + opacity: 0.4, + depthWrite: false, + }); - const area = computeArea(room, "rooms"); - const formattedArea = `${area.toFixed(2)} m²`; + return ( + + + + ); + })} + + {roomsState.length > 0 && + roomsState.flat().map((room: any, index: number) => { + if (!toggleView) return null; + const coordinates = room.coordinates; - const htmlPosition: [number, number, number] = [ - center2D[0], - avgY + CONSTANTS.zoneConfig.height, - center2D[1], - ]; + if (!coordinates || coordinates.length < 3) return null; - return ( - -
    - Room ({formattedArea}) -
    - - ); - })} -
    - ); -} + let coords2D = coordinates.map((p: any) => [ + p.position.x, + p.position.z, + ]); + + const first = coords2D[0]; + const last = coords2D[coords2D.length - 1]; + if (first[0] !== last[0] || first[1] !== last[1]) { + coords2D.push(first); + } + + const polygon = turf.polygon([coords2D]); + const center2D = turf.center(polygon).geometry.coordinates; + + const sumY = coordinates.reduce( + (sum: number, p: any) => sum + p.position.y, + 0 + ); + const avgY = sumY / coordinates.length; + + const area = computeArea(room, "rooms"); + const formattedArea = `${area.toFixed(2)} m²`; + + const htmlPosition: [number, number, number] = [ + center2D[0], + avgY + CONSTANTS.zoneConfig.height, + center2D[1], + ]; + + return ( + +
    + Room ({formattedArea}) +
    + + ); + })} +
    + ); +}; export default CalculateAreaGroup; diff --git a/app/src/modules/builder/groups/floorGroup.tsx b/app/src/modules/builder/groups/floorGroup.tsx index f4d2ee7..2c2a8df 100644 --- a/app/src/modules/builder/groups/floorGroup.tsx +++ b/app/src/modules/builder/groups/floorGroup.tsx @@ -6,7 +6,7 @@ import { useToggleView, useWallVisibility, useUpdateScene, -} from "../../../store/store"; +} from "../../../store/builder/store"; import hideRoof from "../geomentries/roofs/hideRoof"; import hideWalls from "../geomentries/walls/hideWalls"; import addAndUpdateReferencePillar from "../geomentries/pillars/addAndUpdateReferencePillar"; diff --git a/app/src/modules/builder/groups/floorGroupAisle.tsx b/app/src/modules/builder/groups/floorGroupAisle.tsx index 2a6831c..f66ca38 100644 --- a/app/src/modules/builder/groups/floorGroupAisle.tsx +++ b/app/src/modules/builder/groups/floorGroupAisle.tsx @@ -2,7 +2,7 @@ import * as THREE from 'three'; import * as Types from '../../../types/world/worldTypes'; import * as CONSTANTS from '../../../types/world/worldConstants'; import { useThree } from "@react-three/fiber"; -import { useToggleView, useActiveLayer, useSocketStore, useDeletePointOrLine, useUpdateScene, useNewLines, useToolMode } from "../../../store/store"; +import { useToggleView, useActiveLayer, useSocketStore, useDeletePointOrLine, useUpdateScene, useNewLines, useToolMode } from "../../../store/builder/store"; import { useEffect } from "react"; import removeSoloPoint from "../geomentries/points/removeSoloPoint"; import removeReferenceLine from "../geomentries/lines/removeReferenceLine"; diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx index 206d00d..ad0c86e 100644 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ b/app/src/modules/builder/groups/floorItemsGroup.tsx @@ -11,8 +11,7 @@ import { useSelectedItem, useSocketStore, useToggleView, - useTransformMode, -} from "../../../store/store"; +} from "../../../store/builder/store"; import { useEffect } from "react"; import * as THREE from "three"; import * as Types from "../../../types/world/worldTypes"; @@ -59,7 +58,6 @@ const FloorItemsGroup = ({ const { camMode } = useCamMode(); const { deleteTool } = useDeleteTool(); const { setDeletableFloorItem } = useDeletableFloorItem(); - const { transformMode } = useTransformMode(); const { setSelectedFloorItem } = useSelectedFloorItem(); const { activeTool } = useActiveTool(); const { selectedItem, setSelectedItem } = useSelectedItem(); @@ -257,9 +255,8 @@ const FloorItemsGroup = ({ socket ); } - const Mode = transformMode; - if (Mode !== null || activeTool === "cursor") { + if (activeTool === "cursor") { if (!itemsGroup.current) return; let intersects = raycaster.intersectObjects( itemsGroup.current.children, @@ -296,9 +293,8 @@ const FloorItemsGroup = ({ isLeftMouseDown = false; if (drag) return; - const Mode = transformMode; - if (Mode !== null || activeTool === "cursor") { + if (activeTool === "cursor") { if (!itemsGroup.current) return; let intersects = raycaster.intersectObjects( itemsGroup.current.children, @@ -366,7 +362,11 @@ const FloorItemsGroup = ({ const onDrop = (event: any) => { if (!event.dataTransfer?.files[0]) return; - if (selectedItem.id !== "" && event.dataTransfer?.files[0]) { + if (selectedItem.id !== "" && event.dataTransfer?.files[0] && selectedItem.category !== 'Fenestration') { + + state.pointer.x = (event.clientX / window.innerWidth) * 2 - 1; + state.pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; + addAssetModel( raycaster, state.camera, @@ -412,16 +412,7 @@ const FloorItemsGroup = ({ canvasElement.removeEventListener("drop", onDrop); canvasElement.removeEventListener("dragover", onDragOver); }; - }, [ - deleteTool, - transformMode, - controls, - selectedItem, - state.camera, - state.pointer, - activeTool, - activeModule, - ]); + }, [deleteTool, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule,]); useFrame(() => { if (controls) diff --git a/app/src/modules/builder/groups/floorPlanGroup.tsx b/app/src/modules/builder/groups/floorPlanGroup.tsx index c1c38f0..a8832f2 100644 --- a/app/src/modules/builder/groups/floorPlanGroup.tsx +++ b/app/src/modules/builder/groups/floorPlanGroup.tsx @@ -1,6 +1,6 @@ import { useEffect } from "react"; import * as Types from '../../../types/world/worldTypes'; -import { useActiveLayer, useDeletedLines, useDeletePointOrLine, useToolMode, useNewLines, useRemovedLayer, useSocketStore, useToggleView, useUpdateScene } from "../../../store/store"; +import { useActiveLayer, useDeletedLines, useDeletePointOrLine, 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"; diff --git a/app/src/modules/builder/groups/wallItemsGroup.tsx b/app/src/modules/builder/groups/wallItemsGroup.tsx index 76ba502..4d1a2ef 100644 --- a/app/src/modules/builder/groups/wallItemsGroup.tsx +++ b/app/src/modules/builder/groups/wallItemsGroup.tsx @@ -1,13 +1,14 @@ import { useEffect } from "react"; import { - useDeleteTool, - useDeletePointOrLine, - useObjectPosition, - useObjectRotation, - useSelectedWallItem, - useSocketStore, - useWallItems, -} from "../../../store/store"; + useDeleteTool, + useDeletePointOrLine, + useObjectPosition, + useObjectRotation, + useSelectedWallItem, + useSocketStore, + useWallItems, + useSelectedItem, +} from "../../../store/builder/store"; import { Csg } from "../csg/csg"; import * as Types from "../../../types/world/worldTypes"; import * as CONSTANTS from "../../../types/world/worldConstants"; @@ -20,276 +21,259 @@ import AddWallItems from "../geomentries/walls/addWallItems"; import useModuleStore from "../../../store/useModuleStore"; const WallItemsGroup = ({ - currentWallItem, - AssetConfigurations, - hoveredDeletableWallItem, - selectedItemsIndex, - setSelectedItemsIndex, - CSGGroup, + currentWallItem, + hoveredDeletableWallItem, + selectedItemsIndex, + setSelectedItemsIndex, + CSGGroup, }: any) => { - const state = useThree(); - const { socket } = useSocketStore(); - const { pointer, camera, raycaster } = state; - const { deleteTool } = useDeleteTool(); - const { wallItems, setWallItems } = useWallItems(); - const { setObjectPosition } = useObjectPosition(); - const { setObjectRotation } = useObjectRotation(); - const { deletePointOrLine } = useDeletePointOrLine(); - const { setSelectedWallItem } = useSelectedWallItem(); - const { activeModule } = useModuleStore(); + const state = useThree(); + const { socket } = useSocketStore(); + const { pointer, camera, raycaster } = state; + const { deleteTool } = useDeleteTool(); + const { wallItems, setWallItems } = useWallItems(); + const { setObjectPosition } = useObjectPosition(); + const { setObjectRotation } = useObjectRotation(); + const { deletePointOrLine } = useDeletePointOrLine(); + const { setSelectedWallItem } = useSelectedWallItem(); + const { activeModule } = useModuleStore(); + const { selectedItem, setSelectedItem } = useSelectedItem(); - useEffect(() => { - // Load Wall Items from the backend - loadInitialWallItems(setWallItems, AssetConfigurations); - }, []); + useEffect(() => { + // Load Wall Items from the backend + loadInitialWallItems(setWallItems); + }, []); - ////////// Update the Scale value changes in thewallItems State ////////// + ////////// 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 && !deletePointOrLine && e.buttons === 1) { + const Raycaster = state.raycaster; + const intersects = Raycaster.intersectObjects(CSGGroup.current?.children[0].children!, true); + const Object = intersects.find((child) => child.object.name.includes("WallRaycastReference")); - ////////// Update the Rotation value changes in the selected item ////////// + if (Object) { + (state.controls as any)!.enabled = false; + setWallItems((prevItems: any) => { + const updatedItems = [...prevItems]; + let position: [number, number, number] = [0, 0, 0]; - useEffect(() => { - const canvasElement = state.gl.domElement; - function handlePointerMove(e: any) { - if ( - selectedItemsIndex !== null && - !deletePointOrLine && - e.buttons === 1 - ) { - const Raycaster = state.raycaster; - const intersects = Raycaster.intersectObjects( - CSGGroup.current?.children[0].children!, - true - ); - const Object = intersects.find((child) => - child.object.name.includes("WallRaycastReference") - ); + 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 (Object) { - (state.controls as any)!.enabled = false; - setWallItems((prevItems: any) => { - const updatedItems = [...prevItems]; - let position: [number, number, number] = [0, 0, 0]; + 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), + }); + }); - 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]; + updatedItems[selectedItemsIndex] = { + ...updatedItems[selectedItemsIndex], + position: position, + quaternion: Object!.object.quaternion.clone() as Types.QuaternionType, + }; + + return updatedItems; + }); + } } - - 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, - }; - - 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, - }; - }); - - currentItem = updatedItems[selectedItemsIndex]; - localStorage.setItem( - "WallItems", - JSON.stringify(WallItemsForStorage) + async function handlePointerUp() { + const Raycaster = state.raycaster; + const intersects = Raycaster.intersectObjects( + CSGGroup.current?.children[0].children!, + true ); - return updatedItems; - }); + 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, + }; + }); - setTimeout(async () => { - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; + currentItem = updatedItems[selectedItemsIndex]; + localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); + return updatedItems; + }); - //REST + setTimeout(async () => { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; - // await setWallItem( - // organization, - // currentItem?.model?.uuid, - // currentItem.modelName, - // currentItem.type!, - // currentItem.csgposition!, - // currentItem.csgscale!, - // currentItem.position, - // currentItem.quaternion, - // currentItem.scale!, - // ) + //REST - //SOCKET + // await setWallItem( + // organization, + // currentItem?.model?.uuid, + // currentItem.modelName, + // currentItem.modelfileID, + // currentItem.type!, + // currentItem.csgposition!, + // currentItem.csgscale!, + // currentItem.position, + // currentItem.quaternion, + // currentItem.scale!, + // ) - const data = { - organization: organization, - modelUuid: currentItem.model?.uuid!, - modelName: currentItem.modelName!, - type: currentItem.type!, - csgposition: currentItem.csgposition!, - csgscale: currentItem.csgscale!, - position: currentItem.position!, - quaternion: currentItem.quaternion, - scale: currentItem.scale!, - socketId: socket.id, - }; + //SOCKET - socket.emit("v1:wallItems:set", data); - }, 0); - (state.controls as any)!.enabled = true; + const data = { + organization: organization, + modelUuid: currentItem.model?.uuid!, + modelfileID: currentItem.modelfileID, + modelName: currentItem.modelName!, + type: currentItem.type!, + csgposition: currentItem.csgposition!, + csgscale: currentItem.csgscale!, + position: currentItem.position!, + quaternion: currentItem.quaternion, + scale: currentItem.scale!, + socketId: socket.id, + }; + + 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]); + return () => { + canvasElement.removeEventListener("pointermove", handlePointerMove); + canvasElement.removeEventListener("pointerup", handlePointerUp); + }; + }, [selectedItemsIndex]); - 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 && deleteTool && activeModule === "builder") { - DeleteWallItems( - hoveredDeletableWallItem, - setWallItems, - wallItems, - socket - ); + const onMouseUp = (evt: any) => { + if (evt.button === 0) { + isLeftMouseDown = false; + if (!drag && deleteTool && activeModule === "builder") { + DeleteWallItems( + hoveredDeletableWallItem, + setWallItems, + wallItems, + socket + ); + } + } + }; + + const onMouseMove = () => { + if (isLeftMouseDown) { + drag = true; + } + }; + + 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; + + raycaster.setFromCamera(pointer, camera); + + if (selectedItem.id) { + if (selectedItem.subCategory) { + AddWallItems( + selectedItem, + raycaster, + CSGGroup, + setWallItems, + socket + ); + } + 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); + + return () => { + canvasElement.removeEventListener("mousedown", onMouseDown); + canvasElement.removeEventListener("mouseup", onMouseUp); + canvasElement.removeEventListener("mousemove", onMouseMove); + canvasElement.removeEventListener("drop", onDrop); + canvasElement.removeEventListener("dragover", onDragOver); + }; + }, [deleteTool, wallItems, selectedItem, camera]); + + useEffect(() => { + if (deleteTool && activeModule === "builder") { + handleMeshMissed( + currentWallItem, + setSelectedWallItem, + setSelectedItemsIndex + ); + setSelectedWallItem(null); + setSelectedItemsIndex(null); } - } - }; + }, [deleteTool]); - const onMouseMove = () => { - if (isLeftMouseDown) { - drag = true; - } - }; - - const onDrop = (event: any) => { - if (!event.dataTransfer?.files[0]) return; - - pointer.x = (event.clientX / window.innerWidth) * 2 - 1; - pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; - raycaster.setFromCamera(pointer, camera); - - if (AssetConfigurations[event.dataTransfer.files[0].name.split(".")[0]]) { - const selected = event.dataTransfer.files[0].name.split(".")[0]; - - if (AssetConfigurations[selected]?.type) { - AddWallItems( - selected, - raycaster, - CSGGroup, - AssetConfigurations, - setWallItems, - socket - ); - } - 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); - - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - canvasElement.removeEventListener("drop", onDrop); - canvasElement.removeEventListener("dragover", onDragOver); - }; - }, [deleteTool, wallItems]); - - useEffect(() => { - if (deleteTool && activeModule === "builder") { - handleMeshMissed( - currentWallItem, - setSelectedWallItem, - setSelectedItemsIndex - ); - setSelectedWallItem(null); - setSelectedItemsIndex(null); - } - }, [deleteTool]); - - return ( - <> - {wallItems.map((item: Types.WallItem, index: number) => ( - - - - ))} - - ); + return ( + <> + {wallItems.map((item: Types.WallItem, index: number) => ( + + + + ))} + + ); }; export default WallItemsGroup; diff --git a/app/src/modules/builder/groups/wallsAndWallItems.tsx b/app/src/modules/builder/groups/wallsAndWallItems.tsx index 455896e..82acd99 100644 --- a/app/src/modules/builder/groups/wallsAndWallItems.tsx +++ b/app/src/modules/builder/groups/wallsAndWallItems.tsx @@ -1,92 +1,74 @@ import { Geometry } from "@react-three/csg"; import { - useDeleteTool, - useSelectedWallItem, - useToggleView, - useTransformMode, - useWallItems, - useWalls, -} from "../../../store/store"; + useDeleteTool, + useSelectedWallItem, + useToggleView, + 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 { useEffect } from "react"; const WallsAndWallItems = ({ - CSGGroup, - AssetConfigurations, - setSelectedItemsIndex, - selectedItemsIndex, - currentWallItem, - csg, - lines, - hoveredDeletableWallItem, + CSGGroup, + setSelectedItemsIndex, + selectedItemsIndex, + currentWallItem, + csg, + lines, + hoveredDeletableWallItem, }: any) => { - const { walls } = useWalls(); - const { wallItems } = useWallItems(); - const { toggleView } = useToggleView(); - const { deleteTool } = useDeleteTool(); - const { transformMode } = useTransformMode(); - const { setSelectedWallItem } = useSelectedWallItem(); + const { walls } = useWalls(); + const { wallItems } = useWallItems(); + const { toggleView } = useToggleView(); + const { deleteTool } = useDeleteTool(); + const { setSelectedWallItem } = useSelectedWallItem(); - useEffect(() => { - if (transformMode === null) { - if (!deleteTool) { - handleMeshMissed( - currentWallItem, - setSelectedWallItem, - setSelectedItemsIndex - ); - setSelectedWallItem(null); - setSelectedItemsIndex(null); - } - } - }, [transformMode]); - return ( - { - if (!deleteTool && transformMode !== null) { - handleMeshDown( - event, - currentWallItem, - setSelectedWallItem, - setSelectedItemsIndex, - wallItems, - toggleView - ); - } - }} - onPointerMissed={() => { - if (!deleteTool) { - handleMeshMissed( - currentWallItem, - setSelectedWallItem, - setSelectedItemsIndex - ); - setSelectedWallItem(null); - setSelectedItemsIndex(null); - } - }} - > - - - - - - ); + return ( + { + if (!deleteTool) { + handleMeshDown( + event, + currentWallItem, + setSelectedWallItem, + setSelectedItemsIndex, + wallItems, + toggleView + ); + } + }} + onPointerMissed={() => { + if (!deleteTool) { + handleMeshMissed( + currentWallItem, + setSelectedWallItem, + setSelectedItemsIndex + ); + setSelectedWallItem(null); + setSelectedItemsIndex(null); + } + }} + > + + + + + + ); }; export default WallsAndWallItems; diff --git a/app/src/modules/builder/groups/wallsMesh.tsx b/app/src/modules/builder/groups/wallsMesh.tsx index 4f71680..fc0d554 100644 --- a/app/src/modules/builder/groups/wallsMesh.tsx +++ b/app/src/modules/builder/groups/wallsMesh.tsx @@ -3,8 +3,8 @@ 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/store"; -import React, { useEffect } from "react"; +import { useUpdateScene, useWalls } from "../../../store/builder/store"; +import React, { useEffect, useState } from "react"; import { getLines } from "../../../services/factoryBuilder/lines/getLinesApi"; import objectLinesToArray from "../geomentries/lines/lineConvertions/objectLinesToArray"; import loadWalls from "../geomentries/walls/loadWalls"; @@ -15,63 +15,63 @@ import texturePath from "../../../assets/textures/floor/wall-tex.png"; const materialCache = new Map(); const WallsMeshComponent = ({ lines }: any) => { - const { walls, setWalls } = useWalls(); - const { updateScene, setUpdateScene } = useUpdateScene(); + const { walls, setWalls } = useWalls(); + const { updateScene, setUpdateScene } = useUpdateScene(); - useEffect(() => { - if (updateScene) { - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; + useEffect(() => { + if (updateScene) { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; - getLines(organization).then((data) => { - const Lines: Types.Lines = objectLinesToArray(data); - localStorage.setItem("Lines", JSON.stringify(Lines)); + getLines(organization).then((data) => { + const Lines: Types.Lines = objectLinesToArray(data); + localStorage.setItem("Lines", JSON.stringify(Lines)); - if (Lines) { - loadWalls(lines, setWalls); + if (Lines) { + loadWalls(lines, setWalls); + } + }); + setUpdateScene(false); } - }); - setUpdateScene(false); - } - }, [updateScene]); + }, [updateScene]); - 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); diff --git a/app/src/modules/builder/groups/zoneGroup.tsx b/app/src/modules/builder/groups/zoneGroup.tsx index e6c0f1a..4d884dc 100644 --- a/app/src/modules/builder/groups/zoneGroup.tsx +++ b/app/src/modules/builder/groups/zoneGroup.tsx @@ -12,7 +12,7 @@ import { useRemovedLayer, useZones, useZonePoints, -} from "../../../store/store"; +} from "../../../store/builder/store"; import { getZonesApi } from "../../../services/factoryBuilder/zones/getZonesApi"; import * as CONSTANTS from "../../../types/world/worldConstants"; @@ -376,7 +376,6 @@ const ZoneGroup: React.FC = () => { setZonePoints(updatedZonePoints); addZoneToBackend(newZone); - setStartPoint(null); setEndPoint(null); } diff --git a/app/src/modules/builder/layout/layoutImage.tsx b/app/src/modules/builder/layout/layoutImage.tsx new file mode 100644 index 0000000..40c2c3c --- /dev/null +++ b/app/src/modules/builder/layout/layoutImage.tsx @@ -0,0 +1,34 @@ +import { useMemo } from 'react'; +import layout1 from '../../../assets/gltf-glb/layouts/floorplane_1.glb'; +import layout2 from '../../../assets/gltf-glb/layouts/floorplane_2.glb'; +import useLayoutStore from '../../../store/builder/uselayoutStore'; +import { useGLTF } from '@react-three/drei'; +import { useToggleView } from '../../../store/builder/store'; + +const modelPaths: Record = { + 'layout1': layout1, + 'layout2': layout2 +}; + +function LayoutModel() { + const { toggleView } = useToggleView(); + const { currentLayout } = useLayoutStore(); + + // Always call the hook, but ensure it has a valid fallback + const path = currentLayout ? modelPaths[currentLayout] : modelPaths.layout1; + + // Explicitly cast the return type to ensure it's not an array + const gltf = useGLTF(path) as any; + + const cloned = useMemo(() => gltf.scene.clone(), [gltf]); + + if (!currentLayout || !toggleView) return null; + + return ( + + + + ); +} + +export default LayoutModel; diff --git a/app/src/modules/collaboration/camera/collabCams.tsx b/app/src/modules/collaboration/camera/collabCams.tsx index fab6efe..5c4e8d6 100644 --- a/app/src/modules/collaboration/camera/collabCams.tsx +++ b/app/src/modules/collaboration/camera/collabCams.tsx @@ -4,7 +4,7 @@ import { useFrame } from "@react-three/fiber"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import camModel from "../../../assets/gltf-glb/camera face 2.gltf"; import getActiveUsersData from "../../../services/factoryBuilder/collab/getActiveUsers"; -import { useActiveUsers, useSocketStore } from "../../../store/store"; +import { useActiveUsers, useSocketStore } from "../../../store/builder/store"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; import { useNavigate } from "react-router-dom"; import { Html } from "@react-three/drei"; diff --git a/app/src/modules/collaboration/camera/collabUserIcon.tsx b/app/src/modules/collaboration/camera/collabUserIcon.tsx index 28c283f..d3c8f21 100644 --- a/app/src/modules/collaboration/camera/collabUserIcon.tsx +++ b/app/src/modules/collaboration/camera/collabUserIcon.tsx @@ -1,7 +1,7 @@ import React from "react"; import CustomAvatar from "../users/Avatar"; import { useSelectedUserStore } from "../../../store/useCollabStore"; -import { useCamMode } from "../../../store/store"; +import { useCamMode } from "../../../store/builder/store"; interface CollabUserIconProps { userName: string; diff --git a/app/src/modules/collaboration/collaboration.tsx b/app/src/modules/collaboration/collaboration.tsx index ad6af6a..36e7131 100644 --- a/app/src/modules/collaboration/collaboration.tsx +++ b/app/src/modules/collaboration/collaboration.tsx @@ -3,7 +3,7 @@ import CamModelsGroup from "./camera/collabCams"; import { useSelectedUserStore } from "../../store/useCollabStore"; import { useThree } from "@react-three/fiber"; import setCameraView from "./functions/setCameraView"; -import { useCamMode } from "../../store/store"; +import { useCamMode } from "../../store/builder/store"; const Collaboration: React.FC = () => { const { selectedUser } = useSelectedUserStore(); diff --git a/app/src/modules/collaboration/socket/socketResponses.dev.tsx b/app/src/modules/collaboration/socket/socketResponses.dev.tsx index c5d0a5f..441bae4 100644 --- a/app/src/modules/collaboration/socket/socketResponses.dev.tsx +++ b/app/src/modules/collaboration/socket/socketResponses.dev.tsx @@ -1,22 +1,21 @@ import { useEffect } from "react"; import * as THREE from "three"; import gsap from "gsap"; -import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; +import { GLTF, GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; -import { toast } from "react-toastify"; import { - useSocketStore, - useActiveLayer, - useWallItems, - useFloorItems, - useLayers, - useUpdateScene, - useWalls, - useDeletedLines, - useNewLines, - useZonePoints, - useZones, -} from "../../../store/store"; + useSocketStore, + useActiveLayer, + useWallItems, + useFloorItems, + useLayers, + useUpdateScene, + useWalls, + useDeletedLines, + useNewLines, + useZonePoints, + useZones, +} from "../../../store/builder/store"; import * as Types from "../../../types/world/worldTypes"; import * as CONSTANTS from "../../../types/world/worldConstants"; @@ -36,833 +35,863 @@ import { retrieveGLTF, storeGLTF } from "../../../utils/indexDB/idbUtils"; import { getZonesApi } from "../../../services/factoryBuilder/zones/getZonesApi"; export default function SocketResponses({ - floorPlanGroup, - lines, - floorGroup, - floorGroupAisle, - scene, - onlyFloorlines, - AssetConfigurations, - itemsGroup, - isTempLoader, - tempLoader, - currentLayerPoint, - floorPlanGroupPoint, - floorPlanGroupLine, - zoneGroup, - dragPointControls, + floorPlanGroup, + lines, + floorGroup, + floorGroupAisle, + scene, + onlyFloorlines, + itemsGroup, + isTempLoader, + tempLoader, + currentLayerPoint, + floorPlanGroupPoint, + floorPlanGroupLine, + zoneGroup, + dragPointControls, }: any) { - const { socket } = useSocketStore(); - const { activeLayer, setActiveLayer } = useActiveLayer(); - const { wallItems, setWallItems } = useWallItems(); - const { layers, setLayers } = useLayers(); - const { floorItems, setFloorItems } = useFloorItems(); - const { updateScene, setUpdateScene } = useUpdateScene(); - const { walls, setWalls } = useWalls(); - const { deletedLines, setDeletedLines } = useDeletedLines(); - const { newLines, setNewLines } = useNewLines(); - const { zones, setZones } = useZones(); - const { zonePoints, setZonePoints } = useZonePoints(); + const { socket } = useSocketStore(); + const { activeLayer, setActiveLayer } = useActiveLayer(); + const { wallItems, setWallItems } = useWallItems(); + const { setLayers } = useLayers(); + const { setFloorItems } = useFloorItems(); + const { setUpdateScene } = useUpdateScene(); + const { setWalls } = useWalls(); + const { setDeletedLines } = useDeletedLines(); + const { setNewLines } = useNewLines(); + const { zones, setZones } = useZones(); + const { zonePoints, setZonePoints } = useZonePoints(); - useEffect(() => { - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; + useEffect(() => { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; - if (!socket) return; + if (!socket) return; - socket.on("cameraCreateResponse", (data: any) => { - // console.log('data: ', data); - }); + socket.on("cameraCreateResponse", (data: any) => { + // console.log('data: ', data); + }); - socket.on("userConnectRespones", (data: any) => { - // console.log('data: ', data); - }); + socket.on("userConnectRespones", (data: any) => { + // console.log('data: ', data); + }); - socket.on("userDisConnectRespones", (data: any) => { - // console.log('data: ', data); - }); + socket.on("userDisConnectRespones", (data: any) => { + // console.log('data: ', data); + }); - socket.on("cameraUpdateResponse", (data: any) => { - // console.log('data: ', data); - }); + socket.on("cameraUpdateResponse", (data: any) => { + // console.log('data: ', data); + }); - socket.on("EnvironmentUpdateResponse", (data: any) => { - // console.log('data: ', data); - }); + socket.on("EnvironmentUpdateResponse", (data: any) => { + // console.log('data: ', data); + }); - socket.on("model-asset:response:updates", async (data: any) => { - // console.log('data: ', data); - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "Model created successfully") { - const loader = new GLTFLoader(); - const dracoLoader = new DRACOLoader(); - - dracoLoader.setDecoderPath( - "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/" - ); - loader.setDRACOLoader(dracoLoader); - let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - - try { - isTempLoader.current = true; - const cachedModel = THREE.Cache.get(data.data.modelName); - let url; - if (cachedModel) { - // console.log(`Getting ${data.data.modelName} from cache`); - const model = cachedModel.scene.clone(); - model.uuid = data.data.modelUuid; - model.userData = { - name: data.data.modelName, - modelId: data.data.modelfileID, - modelUuid: data.data.modelUuid, - }; - model.position.set( - ...(data.data.position as [number, number, number]) - ); - model.rotation.set( - data.data.rotation.x, - data.data.rotation.y, - data.data.rotation.z - ); - model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); - - model.traverse((child: any) => { - if (child.isMesh) { - // Clone the material to ensure changes are independent - // child.material = child.material.clone(); - - child.castShadow = true; - child.receiveShadow = true; - } - }); - - itemsGroup.current.add(model); - - if (tempLoader.current) { - tempLoader.current.material.dispose(); - tempLoader.current.geometry.dispose(); - itemsGroup.current.remove(tempLoader.current); - tempLoader.current = undefined; + socket.on("model-asset:response:updates", async (data: any) => { + // console.log('data: ', data); + if (socket.id === data.socketId) { + return; } - - const newFloorItem: Types.FloorItemType = { - modelUuid: data.data.modelUuid, - modelName: data.data.modelName, - modelfileID: data.data.modelfileID, - position: [...(data.data.position as [number, number, number])], - rotation: { - x: model.rotation.x, - y: model.rotation.y, - z: model.rotation.z, - }, - isLocked: data.data.isLocked, - isVisible: data.data.isVisible, - }; - - setFloorItems((prevItems: any) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); - - gsap.to(model.position, { - y: data.data.position[1], - duration: 1.5, - ease: "power2.out", - }); - gsap.to(model.scale, { - x: 1, - y: 1, - z: 1, - duration: 1.5, - ease: "power2.out", - onComplete: () => { - toast.success("Model Added!"); - }, - }); - } else { - const indexedDBModel = await retrieveGLTF(data.data.modelName); - if (indexedDBModel) { - // console.log(`Getting ${data.data.modelName} from IndexedDB`); - url = URL.createObjectURL(indexedDBModel); - } else { - // console.log(`Getting ${data.data.modelName} from Backend`); - url = `${url_Backend_dwinzo}/api/v2/AssetFile/${data.data.modelfileID}`; - const modelBlob = await fetch(url).then((res) => res.blob()); - await storeGLTF(data.data.modelfileID, modelBlob); + if (organization !== data.organization) { + return; } - } + if (data.message === "Model created successfully") { + const loader = new GLTFLoader(); + const dracoLoader = new DRACOLoader(); - if (url) { - loadModel(url); - } - } catch (error) { - echo.error("Failed to update responce"); - console.error("Error fetching asset model:", error); - } - - function loadModel(url: string) { - loader.load( - url, - (gltf) => { - URL.revokeObjectURL(url); - THREE.Cache.remove(url); - const model = gltf.scene; - model.uuid = data.data.modelUuid; - model.userData = { - name: data.data.modelName, - modelId: data.data.modelfileID, - modelUuid: data.data.modelUuid, - }; - model.position.set( - ...(data.data.position as [number, number, number]) - ); - model.rotation.set( - data.data.rotation.x, - data.data.rotation.y, - data.data.rotation.z - ); - model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); - - model.traverse((child: any) => { - if (child.isMesh) { - // Clone the material to ensure changes are independent - // child.material = child.material.clone(); - - child.castShadow = true; - child.receiveShadow = true; - } - }); - - itemsGroup.current.add(model); - - if (tempLoader.current) { - tempLoader.current.material.dispose(); - tempLoader.current.geometry.dispose(); - itemsGroup.current.remove(tempLoader.current); - tempLoader.current = undefined; - } - - const newFloorItem: Types.FloorItemType = { - modelUuid: data.data.modelUuid, - modelName: data.data.modelName, - modelfileID: data.data.modelfileID, - position: [...(data.data.position as [number, number, number])], - rotation: { - x: model.rotation.x, - y: model.rotation.y, - z: model.rotation.z, - }, - isLocked: data.data.isLocked, - isVisible: data.data.isVisible, - }; - - setFloorItems((prevItems: any) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem( - "FloorItems", - JSON.stringify(updatedItems) + dracoLoader.setDecoderPath( + "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/" ); - return updatedItems; - }); + loader.setDRACOLoader(dracoLoader); + let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; - gsap.to(model.position, { - y: data.data.position[1], - duration: 1.5, - ease: "power2.out", - }); - gsap.to(model.scale, { - x: 1, - y: 1, - z: 1, - duration: 1.5, - ease: "power2.out", - onComplete: () => { - toast.success("Model Added!"); - }, - }); + try { + isTempLoader.current = true; + const cachedModel = THREE.Cache.get(data.data.modelName); + let url; + if (cachedModel) { + // console.log(`Getting ${data.data.modelName} from cache`); + const model = cachedModel.scene.clone(); + model.uuid = data.data.modelUuid; + model.userData = { + name: data.data.modelName, + modelId: data.data.modelfileID, + modelUuid: data.data.modelUuid, + }; + model.position.set( + ...(data.data.position as [number, number, number]) + ); + model.rotation.set( + data.data.rotation.x, + data.data.rotation.y, + data.data.rotation.z + ); + model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); - THREE.Cache.add(data.data.modelName, gltf); - }, - () => { - TempLoader( - new THREE.Vector3(...data.data.position), - isTempLoader, - tempLoader, - itemsGroup - ); + model.traverse((child: any) => { + if (child.isMesh) { + // Clone the material to ensure changes are independent + // child.material = child.material.clone(); + + child.castShadow = true; + child.receiveShadow = true; + } + }); + + itemsGroup.current.add(model); + + if (tempLoader.current) { + tempLoader.current.material.dispose(); + tempLoader.current.geometry.dispose(); + itemsGroup.current.remove(tempLoader.current); + tempLoader.current = undefined; + } + + const newFloorItem: Types.FloorItemType = { + modelUuid: data.data.modelUuid, + modelName: data.data.modelName, + modelfileID: data.data.modelfileID, + position: [...(data.data.position as [number, number, number])], + rotation: { + x: model.rotation.x, + y: model.rotation.y, + z: model.rotation.z, + }, + isLocked: data.data.isLocked, + isVisible: data.data.isVisible, + }; + + setFloorItems((prevItems: any) => { + const updatedItems = [...(prevItems || []), newFloorItem]; + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; + }); + + gsap.to(model.position, { + y: data.data.position[1], + duration: 1.5, + ease: "power2.out", + }); + gsap.to(model.scale, { + x: 1, + y: 1, + z: 1, + duration: 1.5, + ease: "power2.out", + onComplete: () => { + echo.success("Model Added!"); + }, + }); + } else { + const indexedDBModel = await retrieveGLTF(data.data.modelName); + if (indexedDBModel) { + // console.log(`Getting ${data.data.modelName} from IndexedDB`); + url = URL.createObjectURL(indexedDBModel); + } else { + // console.log(`Getting ${data.data.modelName} from Backend`); + url = `${url_Backend_dwinzo}/api/v2/AssetFile/${data.data.modelfileID}`; + const modelBlob = await fetch(url).then((res) => res.blob()); + await storeGLTF(data.data.modelfileID, modelBlob); + } + } + + if (url) { + loadModel(url); + } + } catch (error) { + echo.error("Failed to update responce"); + console.error("Error fetching asset model:", error); + } + + function loadModel(url: string) { + loader.load( + url, + (gltf) => { + URL.revokeObjectURL(url); + THREE.Cache.remove(url); + const model = gltf.scene; + model.uuid = data.data.modelUuid; + model.userData = { + name: data.data.modelName, + modelId: data.data.modelfileID, + modelUuid: data.data.modelUuid, + }; + model.position.set( + ...(data.data.position as [number, number, number]) + ); + model.rotation.set( + data.data.rotation.x, + data.data.rotation.y, + data.data.rotation.z + ); + model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); + + model.traverse((child: any) => { + if (child.isMesh) { + // Clone the material to ensure changes are independent + // child.material = child.material.clone(); + + child.castShadow = true; + child.receiveShadow = true; + } + }); + + itemsGroup.current.add(model); + + if (tempLoader.current) { + tempLoader.current.material.dispose(); + tempLoader.current.geometry.dispose(); + itemsGroup.current.remove(tempLoader.current); + tempLoader.current = undefined; + } + + const newFloorItem: Types.FloorItemType = { + modelUuid: data.data.modelUuid, + modelName: data.data.modelName, + modelfileID: data.data.modelfileID, + position: [...(data.data.position as [number, number, number])], + rotation: { + x: model.rotation.x, + y: model.rotation.y, + z: model.rotation.z, + }, + isLocked: data.data.isLocked, + isVisible: data.data.isVisible, + }; + + setFloorItems((prevItems: any) => { + const updatedItems = [...(prevItems || []), newFloorItem]; + localStorage.setItem( + "FloorItems", + JSON.stringify(updatedItems) + ); + return updatedItems; + }); + + gsap.to(model.position, { + y: data.data.position[1], + duration: 1.5, + ease: "power2.out", + }); + gsap.to(model.scale, { + x: 1, + y: 1, + z: 1, + duration: 1.5, + ease: "power2.out", + onComplete: () => { + echo.success("Model Added!"); + }, + }); + + THREE.Cache.add(data.data.modelName, gltf); + }, + () => { + TempLoader( + new THREE.Vector3(...data.data.position), + isTempLoader, + tempLoader, + itemsGroup + ); + } + ); + } + } else if (data.message === "Model updated successfully") { + itemsGroup.current?.children.forEach((item: THREE.Group) => { + if (item.uuid === data.data.modelUuid) { + item.position.set( + ...(data.data.position as [number, number, number]) + ); + item.rotation.set( + data.data.rotation.x, + data.data.rotation.y, + data.data.rotation.z + ); + } + }); + + setFloorItems((prevItems: Types.FloorItems) => { + if (!prevItems) { + return; + } + let updatedItem: any = null; + const updatedItems = prevItems.map((item) => { + if (item.modelUuid === data.data.modelUuid) { + updatedItem = { + ...item, + position: [...data.data.position] as [number, number, number], + rotation: { + x: data.data.rotation.x, + y: data.data.rotation.y, + z: data.data.rotation.z, + }, + }; + return updatedItem; + } + return item; + }); + return updatedItems; + }); } - ); - } - } else if (data.message === "Model updated successfully") { - itemsGroup.current?.children.forEach((item: THREE.Group) => { - if (item.uuid === data.data.modelUuid) { - item.position.set( - ...(data.data.position as [number, number, number]) - ); - item.rotation.set( - data.data.rotation.x, - data.data.rotation.y, - data.data.rotation.z - ); - } }); - setFloorItems((prevItems: Types.FloorItems) => { - if (!prevItems) { - return; - } - let updatedItem: any = null; - const updatedItems = prevItems.map((item) => { - if (item.modelUuid === data.data.modelUuid) { - updatedItem = { - ...item, - position: [...data.data.position] as [number, number, number], - rotation: { - x: data.data.rotation.x, - y: data.data.rotation.y, - z: data.data.rotation.z, - }, - }; - return updatedItem; + socket.on("model-asset:response:updates", (data: any) => { + if (socket.id === data.socketId) { + return; } - return item; - }); - return updatedItems; - }); - } - }); - - socket.on("model-asset:response:updates", (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "Model deleted successfully") { - const deletedUUID = data.data.modelUuid; - let items = JSON.parse(localStorage.getItem("FloorItems")!); - - const updatedItems = items.filter( - (item: { modelUuid: string }) => item.modelUuid !== deletedUUID - ); - - const storedItems = JSON.parse( - localStorage.getItem("FloorItems") || "[]" - ); - const updatedStoredItems = storedItems.filter( - (item: { modelUuid: string }) => item.modelUuid !== deletedUUID - ); - localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); - - itemsGroup.current.children.forEach((item: any) => { - if (item.uuid === deletedUUID) { - itemsGroup.current.remove(item); - } - }); - setFloorItems(updatedItems); - toast.success("Model Removed!"); - } - }); - - socket.on("Line:response:update", (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "line updated") { - const DraggedUUID = data.data.uuid; - const DraggedPosition = new THREE.Vector3( - data.data.position.x, - data.data.position.y, - data.data.position.z - ); - - const point = floorPlanGroupPoint.current.getObjectByProperty( - "uuid", - DraggedUUID - ); - point.position.set( - DraggedPosition.x, - DraggedPosition.y, - DraggedPosition.z - ); - const affectedLines = updateLinesPositions( - { uuid: DraggedUUID, position: DraggedPosition }, - lines - ); - - updateLines(floorPlanGroupLine, affectedLines); - updateDistanceText(scene, floorPlanGroupLine, affectedLines); - updateFloorLines(onlyFloorlines, { - uuid: DraggedUUID, - position: DraggedPosition, - }); - - loadWalls(lines, setWalls); - setUpdateScene(true); - } - }); - - socket.on("Line:response:delete", (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "line deleted") { - const line = objectLineToArray(data.data); - const linePoints = line; - const connectedpoints = [linePoints[0][1], linePoints[1][1]]; - - onlyFloorlines.current = onlyFloorlines.current - .map((floorline: any) => - floorline.filter( - (line: any) => - line[0][1] !== connectedpoints[0] && - line[1][1] !== connectedpoints[1] - ) - ) - .filter((floorline: any) => floorline.length > 0); - - const removedLine = lines.current.find( - (item: any) => - (item[0][1] === linePoints[0][1] && - item[1][1] === linePoints[1][1]) || - (item[0][1] === linePoints[1][1] && item[1][1] === linePoints[0][1]) - ); - lines.current = lines.current.filter( - (item: any) => item !== removedLine - ); - - floorPlanGroupLine.current.children.forEach((line: any) => { - const linePoints = line.userData.linePoints as [ - number, - string, - number - ][]; - const uuid1 = linePoints[0][1]; - const uuid2 = linePoints[1][1]; - - if ( - (uuid1 === connectedpoints[0] && uuid2 === connectedpoints[1]) || - (uuid1 === connectedpoints[1] && uuid2 === connectedpoints[0]) - ) { - line.material.dispose(); - line.geometry.dispose(); - floorPlanGroupLine.current.remove(line); - setDeletedLines([line.userData.linePoints]); - } - }); - - connectedpoints.forEach((pointUUID) => { - let isConnected = false; - floorPlanGroupLine.current.children.forEach((line: any) => { - const linePoints = line.userData.linePoints; - const uuid1 = linePoints[0][1]; - const uuid2 = linePoints[1][1]; - if (uuid1 === pointUUID || uuid2 === pointUUID) { - isConnected = true; + if (organization !== data.organization) { + return; } - }); + if (data.message === "Model deleted successfully") { + const deletedUUID = data.data.modelUuid; + let items = JSON.parse(localStorage.getItem("FloorItems")!); - if (!isConnected) { - floorPlanGroupPoint.current.children.forEach((point: any) => { - if (point.uuid === pointUUID) { + const updatedItems = items.filter( + (item: { modelUuid: string }) => item.modelUuid !== deletedUUID + ); + + const storedItems = JSON.parse( + localStorage.getItem("FloorItems") || "[]" + ); + const updatedStoredItems = storedItems.filter( + (item: { modelUuid: string }) => item.modelUuid !== deletedUUID + ); + localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); + + itemsGroup.current.children.forEach((item: any) => { + if (item.uuid === deletedUUID) { + itemsGroup.current.remove(item); + } + }); + setFloorItems(updatedItems); + echo.success("Model Removed!"); + } + }); + + socket.on("Line:response:update", (data: any) => { + if (socket.id === data.socketId) { + return; + } + if (organization !== data.organization) { + return; + } + if (data.message === "line updated") { + const DraggedUUID = data.data.uuid; + const DraggedPosition = new THREE.Vector3( + data.data.position.x, + data.data.position.y, + data.data.position.z + ); + + const point = floorPlanGroupPoint.current.getObjectByProperty( + "uuid", + DraggedUUID + ); + point.position.set( + DraggedPosition.x, + DraggedPosition.y, + DraggedPosition.z + ); + const affectedLines = updateLinesPositions( + { uuid: DraggedUUID, position: DraggedPosition }, + lines + ); + + updateLines(floorPlanGroupLine, affectedLines); + updateDistanceText(scene, floorPlanGroupLine, affectedLines); + updateFloorLines(onlyFloorlines, { + uuid: DraggedUUID, + position: DraggedPosition, + }); + + loadWalls(lines, setWalls); + setUpdateScene(true); + } + }); + + socket.on("Line:response:delete", (data: any) => { + if (socket.id === data.socketId) { + return; + } + if (organization !== data.organization) { + return; + } + if (data.message === "line deleted") { + const line = objectLineToArray(data.data); + const linePoints = line; + const connectedpoints = [linePoints[0][1], linePoints[1][1]]; + + onlyFloorlines.current = onlyFloorlines.current + .map((floorline: any) => + floorline.filter( + (line: any) => + line[0][1] !== connectedpoints[0] && + line[1][1] !== connectedpoints[1] + ) + ) + .filter((floorline: any) => floorline.length > 0); + + const removedLine = lines.current.find( + (item: any) => + (item[0][1] === linePoints[0][1] && + item[1][1] === linePoints[1][1]) || + (item[0][1] === linePoints[1][1] && item[1][1] === linePoints[0][1]) + ); + lines.current = lines.current.filter( + (item: any) => item !== removedLine + ); + + floorPlanGroupLine.current.children.forEach((line: any) => { + const linePoints = line.userData.linePoints as [ + number, + string, + number + ][]; + const uuid1 = linePoints[0][1]; + const uuid2 = linePoints[1][1]; + + if ( + (uuid1 === connectedpoints[0] && uuid2 === connectedpoints[1]) || + (uuid1 === connectedpoints[1] && uuid2 === connectedpoints[0]) + ) { + line.material.dispose(); + line.geometry.dispose(); + floorPlanGroupLine.current.remove(line); + setDeletedLines([line.userData.linePoints]); + } + }); + + connectedpoints.forEach((pointUUID) => { + let isConnected = false; + floorPlanGroupLine.current.children.forEach((line: any) => { + const linePoints = line.userData.linePoints; + const uuid1 = linePoints[0][1]; + const uuid2 = linePoints[1][1]; + if (uuid1 === pointUUID || uuid2 === pointUUID) { + isConnected = true; + } + }); + + if (!isConnected) { + floorPlanGroupPoint.current.children.forEach((point: any) => { + if (point.uuid === pointUUID) { + point.material.dispose(); + point.geometry.dispose(); + floorPlanGroupPoint.current.remove(point); + } + }); + } + }); + + loadWalls(lines, setWalls); + setUpdateScene(true); + + echo.success("Line Removed!"); + } + }); + + socket.on("Line:response:delete:point", (data: any) => { + if (socket.id === data.socketId) { + return; + } + if (organization !== data.organization) { + return; + } + if (data.message === "point deleted") { + const point = floorPlanGroupPoint.current?.getObjectByProperty( + "uuid", + data.data + ); point.material.dispose(); point.geometry.dispose(); floorPlanGroupPoint.current.remove(point); - } - }); - } - }); - loadWalls(lines, setWalls); - setUpdateScene(true); + onlyFloorlines.current = onlyFloorlines.current + .map((floorline: any) => + floorline.filter( + (line: any) => + line[0][1] !== data.data && line[1][1] !== data.data + ) + ) + .filter((floorline: any) => floorline.length > 0); - toast.success("Line Removed!"); - } - }); - - socket.on("Line:response:delete:point", (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "point deleted") { - const point = floorPlanGroupPoint.current?.getObjectByProperty( - "uuid", - data.data - ); - point.material.dispose(); - point.geometry.dispose(); - floorPlanGroupPoint.current.remove(point); - - onlyFloorlines.current = onlyFloorlines.current - .map((floorline: any) => - floorline.filter( - (line: any) => - line[0][1] !== data.data && line[1][1] !== data.data - ) - ) - .filter((floorline: any) => floorline.length > 0); - - RemoveConnectedLines( - data.data, - floorPlanGroupLine, - floorPlanGroupPoint, - setDeletedLines, - lines - ); - - loadWalls(lines, setWalls); - setUpdateScene(true); - - toast.success("Point Removed!"); - } - }); - - socket.on("Line:response:delete:layer", async (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "layer deleted") { - setActiveLayer(1); - const removedLayer = data.data; - const removedLines: Types.Lines = lines.current.filter( - (line: any) => line[0][2] === removedLayer - ); - - ////////// Remove Points and lines from the removed layer ////////// - - removedLines.forEach(async (line) => { - line.forEach(async (removedPoint) => { - const removableLines: THREE.Mesh[] = []; - const connectedpoints: string[] = []; - - floorPlanGroupLine.current.children.forEach((line: any) => { - const linePoints = line.userData.linePoints as [ - number, - string, - number - ][]; - const uuid1 = linePoints[0][1]; - const uuid2 = linePoints[1][1]; - - if (uuid1 === removedPoint[1] || uuid2 === removedPoint[1]) { - connectedpoints.push(uuid1 === removedPoint[1] ? uuid2 : uuid1); - removableLines.push(line as THREE.Mesh); - } - }); - - if (removableLines.length > 0) { - removableLines.forEach((line: any) => { - lines.current = lines.current.filter( - (item: any) => - JSON.stringify(item) !== - JSON.stringify(line.userData.linePoints) + RemoveConnectedLines( + data.data, + floorPlanGroupLine, + floorPlanGroupPoint, + setDeletedLines, + lines ); - line.material.dispose(); - line.geometry.dispose(); - floorPlanGroupLine.current.remove(line); - }); + + loadWalls(lines, setWalls); + setUpdateScene(true); + + echo.success("Point Removed!"); } + }); - const point = floorPlanGroupPoint.current.getObjectByProperty( - "uuid", - removedPoint[1] - ); - if (point) { - point.material.dispose(); - point.geometry.dispose(); - floorPlanGroupPoint.current.remove(point); + socket.on("Line:response:delete:layer", async (data: any) => { + if (socket.id === data.socketId) { + return; + } + if (organization !== data.organization) { + return; + } + if (data.message === "layer deleted") { + setActiveLayer(1); + const removedLayer = data.data; + const removedLines: Types.Lines = lines.current.filter( + (line: any) => line[0][2] === removedLayer + ); + + ////////// Remove Points and lines from the removed layer ////////// + + removedLines.forEach(async (line) => { + line.forEach(async (removedPoint) => { + const removableLines: THREE.Mesh[] = []; + const connectedpoints: string[] = []; + + floorPlanGroupLine.current.children.forEach((line: any) => { + const linePoints = line.userData.linePoints as [ + number, + string, + number + ][]; + const uuid1 = linePoints[0][1]; + const uuid2 = linePoints[1][1]; + + if (uuid1 === removedPoint[1] || uuid2 === removedPoint[1]) { + connectedpoints.push(uuid1 === removedPoint[1] ? uuid2 : uuid1); + removableLines.push(line as THREE.Mesh); + } + }); + + if (removableLines.length > 0) { + removableLines.forEach((line: any) => { + lines.current = lines.current.filter( + (item: any) => + JSON.stringify(item) !== + JSON.stringify(line.userData.linePoints) + ); + line.material.dispose(); + line.geometry.dispose(); + floorPlanGroupLine.current.remove(line); + }); + } + + const point = floorPlanGroupPoint.current.getObjectByProperty( + "uuid", + removedPoint[1] + ); + if (point) { + point.material.dispose(); + point.geometry.dispose(); + floorPlanGroupPoint.current.remove(point); + } + }); + }); + + ////////// Update the remaining lines layer values in the userData and in lines.current ////////// + + let remaining = lines.current.filter( + (line: any) => line[0][2] !== removedLayer + ); + let updatedLines: Types.Lines = []; + remaining.forEach((line: any) => { + let newLines = JSON.parse(JSON.stringify(line)); + if (newLines[0][2] > removedLayer) { + newLines[0][2] -= 1; + newLines[1][2] -= 1; + } + + const matchingLine = floorPlanGroupLine.current.children.find( + (l: any) => + l.userData.linePoints[0][1] === line[0][1] && + l.userData.linePoints[1][1] === line[1][1] + ); + if (matchingLine) { + const updatedUserData = JSON.parse( + JSON.stringify(matchingLine.userData) + ); + updatedUserData.linePoints[0][2] = newLines[0][2]; + updatedUserData.linePoints[1][2] = newLines[1][2]; + matchingLine.userData = updatedUserData; + } + updatedLines.push(newLines); + }); + + lines.current = updatedLines; + localStorage.setItem("Lines", JSON.stringify(lines.current)); + + ////////// Also remove OnlyFloorLines and update it in localstorage ////////// + + onlyFloorlines.current = onlyFloorlines.current.filter((floor: any) => { + return floor[0][0][2] !== removedLayer; + }); + const meshToRemove = floorGroup.current?.children.find( + (mesh: any) => mesh.name === `Only_Floor_Line_${removedLayer}` + ); + if (meshToRemove) { + meshToRemove.geometry.dispose(); + meshToRemove.material.dispose(); + floorGroup.current?.remove(meshToRemove); + } + + const zonesData = await getZonesApi(organization); + const highestLayer = Math.max( + 1, + lines.current.reduce( + (maxLayer: number, segment: any) => + Math.max(maxLayer, segment.layer || 0), + 0 + ), + zonesData.data.reduce( + (maxLayer: number, zone: any) => + Math.max(maxLayer, zone.layer || 0), + 0 + ) + ); + + setLayers(highestLayer); + + loadWalls(lines, setWalls); + setUpdateScene(true); + + echo.success("Layer Removed!"); + } + }); + }, [socket]); + + useEffect(() => { + if (!socket) return; + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; + let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; + + socket.on("wallItemsDeleteResponse", (data: any) => { + if (socket.id === data.socketId) { + return; + } + if (organization !== data.organization) { + return; + } + if (data.message === "wallitem deleted") { + const deletedUUID = data.data.modelUuid; + let WallItemsRef = wallItems; + const Items = WallItemsRef.filter((item: any) => item.model?.uuid !== deletedUUID); + + setWallItems([]); + setTimeout(async () => { + WallItemsRef = Items; + setWallItems(WallItemsRef); + const WallItemsForStorage = WallItemsRef.map((item: any) => { + const { model, ...rest } = item; + return { + ...rest, + modelUuid: model?.uuid, + }; + }); + + localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); + echo.success("Model Removed!"); + }, 50); } - }); }); - ////////// Update the remaining lines layer values in the userData and in lines.current ////////// + socket.on("wallItemsUpdateResponse", (data: any) => { + if (socket.id === data.socketId) { + return; + } + if (organization !== data.organization) { + return; + } + if (data.message === "wallIitem created") { + const loader = new GLTFLoader(); + const dracoLoader = new DRACOLoader(); - let remaining = lines.current.filter( - (line: any) => line[0][2] !== removedLayer - ); - let updatedLines: Types.Lines = []; - remaining.forEach((line: any) => { - let newLines = JSON.parse(JSON.stringify(line)); - if (newLines[0][2] > removedLayer) { - newLines[0][2] -= 1; - newLines[1][2] -= 1; - } + dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/'); + loader.setDRACOLoader(dracoLoader); - const matchingLine = floorPlanGroupLine.current.children.find( - (l: any) => - l.userData.linePoints[0][1] === line[0][1] && - l.userData.linePoints[1][1] === line[1][1] - ); - if (matchingLine) { - const updatedUserData = JSON.parse( - JSON.stringify(matchingLine.userData) - ); - updatedUserData.linePoints[0][2] = newLines[0][2]; - updatedUserData.linePoints[1][2] = newLines[1][2]; - matchingLine.userData = updatedUserData; - } - updatedLines.push(newLines); + // Check THREE.js cache first + const cachedModel = THREE.Cache.get(data.data.modelfileID); + if (cachedModel) { + handleModelLoad(cachedModel); + return; + } + + // Check IndexedDB cache + retrieveGLTF(data.data.modelfileID).then((cachedModelBlob) => { + if (cachedModelBlob) { + const blobUrl = URL.createObjectURL(cachedModelBlob); + loader.load(blobUrl, (gltf) => { + URL.revokeObjectURL(blobUrl); + THREE.Cache.remove(blobUrl); + THREE.Cache.add(data.data.modelfileID, gltf); + handleModelLoad(gltf); + }); + return; + } + }) + + // Load from backend if not in any cache + loader.load(`${url_Backend_dwinzo}/api/v2/AssetFile/${data.data.modelfileID}`, async (gltf) => { + try { + const modelBlob = await fetch(`${url_Backend_dwinzo}/api/v2/AssetFile/${data.data.modelfileID}`).then((res) => res.blob()); + await storeGLTF(data.data.modelfileID, modelBlob); + THREE.Cache.add(data.data.modelfileID, gltf); + await handleModelLoad(gltf); + } catch (error) { + console.error('Failed to cache model:', error); + handleModelLoad(gltf); + } + }); + + async function handleModelLoad(gltf: GLTF) { + const model = gltf.scene.clone(); + model.uuid = data.data.modelUuid; + + model.children[0].children.forEach((child) => { + if (child.name !== "CSG_REF") { + child.castShadow = true; + child.receiveShadow = true; + } + }); + + const newWallItem = { + type: data.data.type, + model: model, + modelName: data.data.modelName, + modelfileID: data.data.modelfileID, + scale: data.data.scale, + csgscale: data.data.csgscale, + csgposition: data.data.csgposition, + position: data.data.position, + quaternion: data.data.quaternion, + }; + + setWallItems((prevItems: Types.wallItems) => { + const updatedItems = [...prevItems, newWallItem]; + + const WallItemsForStorage = updatedItems.map(item => { + const { model, ...rest } = item; + return { + ...rest, + modelUuid: model?.uuid, + }; + }); + + localStorage.setItem("WallItems", JSON.stringify(WallItemsForStorage)); + echo.success("Model Added!"); + return updatedItems; + }); + } + + } else if (data.message === "wallIitem updated") { + const updatedUUID = data.data.modelUuid; + + setWallItems((prevItems: any) => { + const updatedItems = prevItems.map((item: any) => { + if (item.model.uuid === updatedUUID) { + return { + ...item, + position: data.data.position, + quaternion: data.data.quaternion, + scale: data.data.scale, + csgscale: data.data.csgscale, + csgposition: data.data.csgposition, + }; + } + return item; + }); + + const WallItemsForStorage = updatedItems.map((item: any) => { + const { model, ...rest } = item; + return { + ...rest, + modelUuid: model?.uuid, + }; + }); + + localStorage.setItem( + "WallItems", + JSON.stringify(WallItemsForStorage) + ); + echo.success("Model Updated!"); + + return updatedItems; + }); + } }); - lines.current = updatedLines; - localStorage.setItem("Lines", JSON.stringify(lines.current)); + return () => { + socket.off("wallItemsDeleteResponse"); + socket.off("wallItemsUpdateResponse"); + }; + }, [wallItems]); - ////////// Also remove OnlyFloorLines and update it in localstorage ////////// - - onlyFloorlines.current = onlyFloorlines.current.filter((floor: any) => { - return floor[0][0][2] !== removedLayer; - }); - const meshToRemove = floorGroup.current?.children.find( - (mesh: any) => mesh.name === `Only_Floor_Line_${removedLayer}` - ); - if (meshToRemove) { - meshToRemove.geometry.dispose(); - meshToRemove.material.dispose(); - floorGroup.current?.remove(meshToRemove); + function getPointColor(lineType: string | undefined): string { + switch (lineType) { + case CONSTANTS.lineConfig.wallName: + return CONSTANTS.pointConfig.wallOuterColor; + case CONSTANTS.lineConfig.floorName: + return CONSTANTS.pointConfig.floorOuterColor; + case CONSTANTS.lineConfig.aisleName: + return CONSTANTS.pointConfig.aisleOuterColor; + default: + return CONSTANTS.pointConfig.defaultOuterColor; } + } - const zonesData = await getZonesApi(organization); - const highestLayer = Math.max( - 1, - lines.current.reduce( - (maxLayer: number, segment: any) => - Math.max(maxLayer, segment.layer || 0), - 0 - ), - zonesData.data.reduce( - (maxLayer: number, zone: any) => - Math.max(maxLayer, zone.layer || 0), - 0 - ) - ); + function getLineColor(lineType: string | undefined): string { + switch (lineType) { + case CONSTANTS.lineConfig.wallName: + return CONSTANTS.lineConfig.wallColor; + case CONSTANTS.lineConfig.floorName: + return CONSTANTS.lineConfig.floorColor; + case CONSTANTS.lineConfig.aisleName: + return CONSTANTS.lineConfig.aisleColor; + default: + return CONSTANTS.lineConfig.defaultColor; + } + } - setLayers(highestLayer); + useEffect(() => { + if (!socket) return; + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; - loadWalls(lines, setWalls); - setUpdateScene(true); - - toast.success("Layer Removed!"); - } - }); - }, [socket]); - - useEffect(() => { - if (!socket) return; - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; - - socket.on("wallItemsDeleteResponse", (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "wallitem deleted") { - const deletedUUID = data.data.modelUuid; - let WallItemsRef = wallItems; - const Items = WallItemsRef.filter( - (item: any) => item.model?.uuid !== deletedUUID - ); - - setWallItems([]); - setTimeout(async () => { - WallItemsRef = Items; - setWallItems(WallItemsRef); - const WallItemsForStorage = WallItemsRef.map((item: any) => { - const { model, ...rest } = item; - return { - ...rest, - modelUuid: model?.uuid, - }; - }); - - localStorage.setItem( - "WallItems", - JSON.stringify(WallItemsForStorage) - ); - toast.success("Model Removed!"); - }, 50); - } - }); - - socket.on("wallItemsUpdateResponse", (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "wallIitem created") { - const loader = new GLTFLoader(); - loader.load( - AssetConfigurations[data.data.modelName].modelUrl, - async (gltf) => { - const model = gltf.scene; - model.uuid = data.data.modelUuid; - model.children[0].children.forEach((child) => { - if (child.name !== "CSG_REF") { - child.castShadow = true; - child.receiveShadow = true; - } - }); - - const newWallItem = { - type: data.data.type, - model: model, - modelName: data.data.modelName, - scale: data.data.scale, - csgscale: data.data.csgscale, - csgposition: data.data.csgposition, - position: data.data.position, - quaternion: data.data.quaternion, - }; - - setWallItems((prevItems: any) => { - const updatedItems = [...prevItems, newWallItem]; - - const WallItemsForStorage = updatedItems.map((item) => { - const { model, ...rest } = item; - return { - ...rest, - modelUuid: model?.uuid, - }; - }); - - localStorage.setItem( - "WallItems", - JSON.stringify(WallItemsForStorage) - ); - toast.success("Model Added!"); - - return updatedItems; - }); - } - ); - } else if (data.message === "wallIitem updated") { - const updatedUUID = data.data.modelUuid; - - setWallItems((prevItems: any) => { - const updatedItems = prevItems.map((item: any) => { - if (item.model.uuid === updatedUUID) { - return { - ...item, - position: data.data.position, - quaternion: data.data.quaternion, - scale: data.data.scale, - csgscale: data.data.csgscale, - csgposition: data.data.csgposition, - }; + socket.on("Line:response:create", async (data: any) => { + if (socket.id === data.socketId) { + return; } - return item; - }); + if (organization !== data.organization) { + return; + } + if (data.message === "line create") { + const line: Types.Line = objectLineToArray(data.data); + const type = line[0][3]; + const pointColour = getPointColor(type); + const lineColour = getLineColor(type); + setNewLines([line]); - const WallItemsForStorage = updatedItems.map((item: any) => { - const { model, ...rest } = item; - return { - ...rest, - modelUuid: model?.uuid, - }; - }); - - localStorage.setItem( - "WallItems", - JSON.stringify(WallItemsForStorage) - ); - toast.success("Model Updated!"); - - return updatedItems; - }); - } - }); - - return () => { - socket.off("wallItemsDeleteResponse"); - socket.off("wallItemsUpdateResponse"); - }; - }, [wallItems]); - - function getPointColor(lineType: string | undefined): string { - switch (lineType) { - case CONSTANTS.lineConfig.wallName: - return CONSTANTS.pointConfig.wallOuterColor; - case CONSTANTS.lineConfig.floorName: - return CONSTANTS.pointConfig.floorOuterColor; - case CONSTANTS.lineConfig.aisleName: - return CONSTANTS.pointConfig.aisleOuterColor; - default: - return CONSTANTS.pointConfig.defaultOuterColor; - } - } - - function getLineColor(lineType: string | undefined): string { - switch (lineType) { - case CONSTANTS.lineConfig.wallName: - return CONSTANTS.lineConfig.wallColor; - case CONSTANTS.lineConfig.floorName: - return CONSTANTS.lineConfig.floorColor; - case CONSTANTS.lineConfig.aisleName: - return CONSTANTS.lineConfig.aisleColor; - default: - return CONSTANTS.lineConfig.defaultColor; - } - } - - useEffect(() => { - if (!socket) return; - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; - - socket.on("Line:response:create", async (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "line create") { - const line: Types.Line = objectLineToArray(data.data); - const type = line[0][3]; - const pointColour = getPointColor(type); - const lineColour = getLineColor(type); - setNewLines([line]); - - line.forEach((line) => { - const existingPoint = - floorPlanGroupPoint.current?.getObjectByProperty("uuid", line[1]); - if (existingPoint) { - return; - } - const geometry = new THREE.BoxGeometry( - ...CONSTANTS.pointConfig.boxScale - ); - const material = new THREE.ShaderMaterial({ - uniforms: { - uColor: { value: new THREE.Color(pointColour) }, // Blue color for the border - uInnerColor: { - value: new THREE.Color(CONSTANTS.pointConfig.defaultInnerColor), - }, // White color for the inner square - }, - vertexShader: ` + line.forEach((line) => { + const existingPoint = + floorPlanGroupPoint.current?.getObjectByProperty("uuid", line[1]); + if (existingPoint) { + return; + } + const geometry = new THREE.BoxGeometry( + ...CONSTANTS.pointConfig.boxScale + ); + const material = new THREE.ShaderMaterial({ + uniforms: { + uColor: { value: new THREE.Color(pointColour) }, // Blue color for the border + uInnerColor: { + value: new THREE.Color(CONSTANTS.pointConfig.defaultInnerColor), + }, // White color for the inner square + }, + vertexShader: ` varying vec2 vUv; void main() { @@ -870,7 +899,7 @@ export default function SocketResponses({ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, - fragmentShader: ` + fragmentShader: ` varying vec2 vUv; uniform vec3 uColor; uniform vec3 uInnerColor; @@ -886,162 +915,162 @@ export default function SocketResponses({ } } `, - }); - const point = new THREE.Mesh(geometry, material); - point.name = "point"; - point.uuid = line[1]; - point.userData = { type: type, color: pointColour }; - point.position.set(line[0].x, line[0].y, line[0].z); - currentLayerPoint.current.push(point); + }); + const point = new THREE.Mesh(geometry, material); + point.name = "point"; + point.uuid = line[1]; + point.userData = { type: type, color: pointColour }; + point.position.set(line[0].x, line[0].y, line[0].z); + currentLayerPoint.current.push(point); - floorPlanGroupPoint.current?.add(point); + floorPlanGroupPoint.current?.add(point); + }); + if (dragPointControls.current) { + dragPointControls.current!.objects = currentLayerPoint.current; + } + addLineToScene( + new THREE.Vector3(line[0][0].x, line[0][0].y, line[0][0].z), + new THREE.Vector3(line[1][0].x, line[1][0].y, line[1][0].z), + lineColour, + line, + floorPlanGroupLine + ); + lines.current.push(line); + + const zonesData = await getZonesApi(organization); + const highestLayer = Math.max( + 1, + lines.current.reduce( + (maxLayer: number, segment: any) => + Math.max(maxLayer, segment.layer || 0), + 0 + ), + zonesData.data.reduce( + (maxLayer: number, zone: any) => + Math.max(maxLayer, zone.layer || 0), + 0 + ) + ); + + setLayers(highestLayer); + + Layer2DVisibility( + activeLayer, + floorPlanGroup, + floorPlanGroupLine, + floorPlanGroupPoint, + currentLayerPoint, + dragPointControls + ); + + loadWalls(lines, setWalls); + setUpdateScene(true); + } }); - if (dragPointControls.current) { - dragPointControls.current!.objects = currentLayerPoint.current; - } - addLineToScene( - new THREE.Vector3(line[0][0].x, line[0][0].y, line[0][0].z), - new THREE.Vector3(line[1][0].x, line[1][0].y, line[1][0].z), - lineColour, - line, - floorPlanGroupLine - ); - lines.current.push(line); - const zonesData = await getZonesApi(organization); - const highestLayer = Math.max( - 1, - lines.current.reduce( - (maxLayer: number, segment: any) => - Math.max(maxLayer, segment.layer || 0), - 0 - ), - zonesData.data.reduce( - (maxLayer: number, zone: any) => - Math.max(maxLayer, zone.layer || 0), - 0 - ) - ); + return () => { + socket.off("Line:response:create"); + }; + }, [socket, activeLayer]); - setLayers(highestLayer); + useEffect(() => { + if (!socket) return; + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; - Layer2DVisibility( - activeLayer, - floorPlanGroup, - floorPlanGroupLine, - floorPlanGroupPoint, - currentLayerPoint, - dragPointControls - ); + socket.on("zone:response:updates", (data: any) => { + if (socket.id === data.socketId) { + return; + } + if (organization !== data.organization) { + return; + } - loadWalls(lines, setWalls); - setUpdateScene(true); - } - }); + if (data.message === "zone created") { + const pointsArray: [number, number, number][] = data.data.points; + const vector3Array = pointsArray.map( + ([x, y, z]) => new THREE.Vector3(x, y, z) + ); + const newZones = [...zones, data.data]; + setZones(newZones); + const updatedZonePoints = [...zonePoints, ...vector3Array]; + setZonePoints(updatedZonePoints); - return () => { - socket.off("Line:response:create"); - }; - }, [socket, activeLayer]); + const highestLayer = Math.max( + 1, + lines.current.reduce( + (maxLayer: number, segment: any) => + Math.max(maxLayer, segment.layer || 0), + 0 + ), + newZones.reduce( + (maxLayer: number, zone: any) => + Math.max(maxLayer, zone.layer || 0), + 0 + ) + ); - useEffect(() => { - if (!socket) return; - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; + setLayers(highestLayer); + setUpdateScene(true); + } - socket.on("zone:response:updates", (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } + if (data.message === "zone updated") { + const updatedZones = zones.map((zone: any) => + zone.zoneId === data.data.zoneId ? data.data : zone + ); + setZones(updatedZones); + setUpdateScene(true); + } + }); - if (data.message === "zone created") { - const pointsArray: [number, number, number][] = data.data.points; - const vector3Array = pointsArray.map( - ([x, y, z]) => new THREE.Vector3(x, y, z) - ); - const newZones = [...zones, data.data]; - setZones(newZones); - const updatedZonePoints = [...zonePoints, ...vector3Array]; - setZonePoints(updatedZonePoints); + socket.on("zone:response:delete", (data: any) => { + if (socket.id === data.socketId) { + return; + } + if (organization !== data.organization) { + return; + } + if (data.message === "zone deleted") { + const updatedZones = zones.filter( + (zone: any) => zone.zoneId !== data.data.zoneId + ); + setZones(updatedZones); - const highestLayer = Math.max( - 1, - lines.current.reduce( - (maxLayer: number, segment: any) => - Math.max(maxLayer, segment.layer || 0), - 0 - ), - newZones.reduce( - (maxLayer: number, zone: any) => - Math.max(maxLayer, zone.layer || 0), - 0 - ) - ); + const zoneIndex = zones.findIndex( + (zone: any) => zone.zoneId === data.data.zoneId + ); + if (zoneIndex !== -1) { + const updatedzonePoints = zonePoints.filter( + (_: any, index: any) => + index < zoneIndex * 4 || index >= zoneIndex * 4 + 4 + ); + setZonePoints(updatedzonePoints); + } - setLayers(highestLayer); - setUpdateScene(true); - } + const highestLayer = Math.max( + 1, + lines.current.reduce( + (maxLayer: number, segment: any) => + Math.max(maxLayer, segment.layer || 0), + 0 + ), + updatedZones.reduce( + (maxLayer: number, zone: any) => + Math.max(maxLayer, zone.layer || 0), + 0 + ) + ); - if (data.message === "zone updated") { - const updatedZones = zones.map((zone: any) => - zone.zoneId === data.data.zoneId ? data.data : zone - ); - setZones(updatedZones); - setUpdateScene(true); - } - }); + setLayers(highestLayer); + setUpdateScene(true); + } + }); - socket.on("zone:response:delete", (data: any) => { - if (socket.id === data.socketId) { - return; - } - if (organization !== data.organization) { - return; - } - if (data.message === "zone deleted") { - const updatedZones = zones.filter( - (zone: any) => zone.zoneId !== data.data.zoneId - ); - setZones(updatedZones); + return () => { + socket.off("zone:response:updates"); + socket.off("zone:response:delete"); + }; + }, [socket, zones, zonePoints]); - const zoneIndex = zones.findIndex( - (zone: any) => zone.zoneId === data.data.zoneId - ); - if (zoneIndex !== -1) { - const updatedzonePoints = zonePoints.filter( - (_: any, index: any) => - index < zoneIndex * 4 || index >= zoneIndex * 4 + 4 - ); - setZonePoints(updatedzonePoints); - } - - const highestLayer = Math.max( - 1, - lines.current.reduce( - (maxLayer: number, segment: any) => - Math.max(maxLayer, segment.layer || 0), - 0 - ), - updatedZones.reduce( - (maxLayer: number, zone: any) => - Math.max(maxLayer, zone.layer || 0), - 0 - ) - ); - - setLayers(highestLayer); - setUpdateScene(true); - } - }); - - return () => { - socket.off("zone:response:updates"); - socket.off("zone:response:delete"); - }; - }, [socket, zones, zonePoints]); - - return <>; + return <>; } diff --git a/app/src/modules/market/AssetPreview.tsx b/app/src/modules/market/AssetPreview.tsx index aa037e2..c2bb0d9 100644 --- a/app/src/modules/market/AssetPreview.tsx +++ b/app/src/modules/market/AssetPreview.tsx @@ -65,7 +65,7 @@ const AssetPreview: React.FC = ({ > }> {selectedCard.assetName && modelUrl && ( - + )} = ({ views, image, description, - onSelectCard, AssetID, + modelUrl, + onSelectCard, + }) => { const handleCardSelect = () => { + console.log('assetName: ', assetName); + console.log('AssetID: ', AssetID); + onSelectCard({ - assetName, - uploadedOn, - price, - rating, - views, - description, - AssetID, + assetName, uploadedOn, price, rating, views, description, AssetID }); }; diff --git a/app/src/modules/market/CardsContainer.tsx b/app/src/modules/market/CardsContainer.tsx index 684fc1d..5612b6b 100644 --- a/app/src/modules/market/CardsContainer.tsx +++ b/app/src/modules/market/CardsContainer.tsx @@ -66,6 +66,7 @@ const CardsContainer: React.FC = ({ models }) => { AssetID={assetDetail.AssetID} image={assetDetail.thumbnail} description={assetDetail.description} + modelUrl={modelUrl} /> ))} diff --git a/app/src/modules/market/GltfLoader.tsx b/app/src/modules/market/GltfLoader.tsx index cfec06d..387b4b8 100644 --- a/app/src/modules/market/GltfLoader.tsx +++ b/app/src/modules/market/GltfLoader.tsx @@ -10,6 +10,7 @@ interface GltfLoaderProps { setAnimations?: (animations?: string[]) => void; selectedAnimation?: string; setSelectedAnimation?: (animation: string) => void; + assetId: string } @@ -18,6 +19,7 @@ const GltfLoader: React.FC = ({ fromServer, setAnimations, selectedAnimation, + assetId }) => { const { scene, animations } = useGLTF(fromServer ?? "") as { scene: Object3D; diff --git a/app/src/modules/market/MarketPlace.tsx b/app/src/modules/market/MarketPlace.tsx index c76baa3..8440467 100644 --- a/app/src/modules/market/MarketPlace.tsx +++ b/app/src/modules/market/MarketPlace.tsx @@ -33,6 +33,7 @@ const MarketPlace = () => { try { const filt = await getAssetImages("67d934ad0f42a1fdadb19aa6"); setModels(filt.items); + console.log('filt.items: ', filt.items); setFilteredModels(filt.items); setisLoading(false); } catch { diff --git a/app/src/modules/scene/camera/camMode.tsx b/app/src/modules/scene/camera/camMode.tsx index 0a0f954..8db0b1b 100644 --- a/app/src/modules/scene/camera/camMode.tsx +++ b/app/src/modules/scene/camera/camMode.tsx @@ -1,7 +1,7 @@ import { useFrame, useThree } from "@react-three/fiber"; import React, { useEffect, useState } from "react"; import * as CONSTANTS from "../../../types/world/worldConstants"; -import { useCamMode, useToggleView } from "../../../store/store"; +import { useCamMode, useToggleView } from "../../../store/builder/store"; import { useKeyboardControls } from "@react-three/drei"; import switchToThirdPerson from "./switchToThirdPerson"; import switchToFirstPerson from "./switchToFirstPerson"; diff --git a/app/src/modules/scene/camera/switchView.tsx b/app/src/modules/scene/camera/switchView.tsx index 0fa8921..ba631a7 100644 --- a/app/src/modules/scene/camera/switchView.tsx +++ b/app/src/modules/scene/camera/switchView.tsx @@ -1,6 +1,6 @@ import * as THREE from "three"; import { useEffect, useRef } from "react"; -import { useToggleView } from "../../../store/store"; +import { useToggleView } from "../../../store/builder/store"; import { useThree } from "@react-three/fiber"; import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi"; import * as CONSTANTS from '../../../types/world/worldConstants'; diff --git a/app/src/modules/scene/controls/controls.tsx b/app/src/modules/scene/controls/controls.tsx index 8bdd3aa..73fbe95 100644 --- a/app/src/modules/scene/controls/controls.tsx +++ b/app/src/modules/scene/controls/controls.tsx @@ -4,12 +4,13 @@ import { useThree } from "@react-three/fiber"; import * as THREE from "three"; import * as CONSTANTS from '../../../types/world/worldConstants'; -import { useSocketStore, useToggleView, useResetCamera } from "../../../store/store"; +import { useSocketStore, useToggleView, useResetCamera } from "../../../store/builder/store"; import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi"; import updateCamPosition from "../camera/updateCameraPosition"; import CamMode from "../camera/camMode"; import SwitchView from "../camera/switchView"; import SelectionControls from "./selectionControls/selectionControls"; +import TransformControl from "./transformControls/transformControls"; export default function Controls() { const controlsRef = useRef(null); @@ -138,6 +139,8 @@ export default function Controls() { + + ); } \ No newline at end of file diff --git a/app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx b/app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx index df57bf9..5802705 100644 --- a/app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx +++ b/app/src/modules/scene/controls/selectionControls/boundingBoxHelper.tsx @@ -1,7 +1,7 @@ import { Line } from "@react-three/drei"; import { useMemo } from "react"; import * as THREE from "three"; -import { useSelectedAssets } from "../../../../store/store"; +import { useSelectedAssets } from "../../../../store/builder/store"; const BoundingBox = ({ boundingBoxRef }: any) => { const { selectedAssets } = useSelectedAssets(); diff --git a/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx b/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx index 82f21ab..dd52900 100644 --- a/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx @@ -1,8 +1,7 @@ import * as THREE from "three"; import { useEffect, useMemo } from "react"; import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/store"; -import { toast } from "react-toastify"; +import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; @@ -95,7 +94,7 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas return clone; }); setCopiedObjects(newClones); - toast.info("Objects copied!"); + echo.info("Objects copied!"); } }; @@ -415,7 +414,7 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas } }); - toast.success("Object added!"); + echo.success("Object added!"); clearSelection(); }; diff --git a/app/src/modules/scene/controls/selectionControls/distanceFindingControls.tsx b/app/src/modules/scene/controls/selectionControls/distanceFindingControls.tsx index 25e6c96..4f2eb02 100644 --- a/app/src/modules/scene/controls/selectionControls/distanceFindingControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/distanceFindingControls.tsx @@ -1,4 +1,4 @@ -import React, { useRef } from "react"; +import React, { useRef, useState } from "react"; import { Vector3, Raycaster, @@ -21,6 +21,17 @@ const DistanceFindingControls = ({ object, }: DistanceFindingControlsProps) => { const { camera, scene } = useThree(); + const [labelValues, setLabelValues] = useState<{ + textPosX: any; + textNegX: any; + textPosZ: any; + textNegZ: any; + }>({ + textPosX: "", + textNegX: "", + textPosZ: "", + textNegZ: "", + }); // Refs for measurement lines const line1 = useRef(null); @@ -167,13 +178,14 @@ const DistanceFindingControls = ({ } } } - - // Update line geometry 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(); @@ -181,9 +193,28 @@ const DistanceFindingControls = ({ if (center) { mesh.current.position.copy(center); } + const label = document.getElementById(mesh.current.name); if (label) { - label.innerText = `${points[0].distanceTo(points[1]).toFixed(2)}m`; + 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 { @@ -191,9 +222,31 @@ const DistanceFindingControls = ({ geometry.dispose(); geometry.setFromPoints([new Vector3(), new Vector3()]); line.geometry = geometry; + const label = document.getElementById(mesh?.current?.name ?? ""); - if (label) label.innerText = ""; + 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" }); @@ -208,39 +261,54 @@ const DistanceFindingControls = ({ wrapperClass="distance-text-wrapper" className="distance-text" zIndexRange={[1, 0]} - style={{ pointerEvents: "none" }} + style={{ + pointerEvents: "none", + visibility: labelValues.textPosX === "" ? "hidden" : "visible", + }} > -
    +
    {labelValues.textPosX}
    + -
    +
    {labelValues.textNegX}
    + -
    +
    {labelValues.textPosZ}
    + -
    +
    {labelValues.textNegZ}
    @@ -262,7 +330,8 @@ const DistanceFindingControls = ({ ref={line4} /> - )} + ) + } ); }; diff --git a/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx b/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx index 3e951a3..2dd9e37 100644 --- a/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx @@ -1,8 +1,7 @@ import * as THREE from "three"; import { useEffect, useMemo } from "react"; import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/store"; -import { toast } from "react-toastify"; +import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; @@ -392,7 +391,7 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb } }); - toast.success("Object duplicated!"); + echo.success("Object duplicated!"); clearSelection(); } diff --git a/app/src/modules/scene/controls/selectionControls/moveControls.tsx b/app/src/modules/scene/controls/selectionControls/moveControls.tsx index 18af918..f5e70f9 100644 --- a/app/src/modules/scene/controls/selectionControls/moveControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/moveControls.tsx @@ -2,13 +2,12 @@ import * as THREE from "three"; import { useEffect, useMemo, useRef, useState } from "react"; import { useFrame, useThree } from "@react-three/fiber"; import { - useFloorItems, - useSelectedAssets, - useSocketStore, - useToggleView, -} from "../../../../store/store"; + useFloorItems, + useSelectedAssets, + useSocketStore, + useToggleView, +} from "../../../../store/builder/store"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; -import { toast } from "react-toastify"; import * as Types from "../../../../types/world/worldTypes"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; import { useEventsStore } from "../../../../store/simulation/useEventsStore"; @@ -19,370 +18,337 @@ import { snapControls } from "../../../../utils/handleSnap"; import DistanceFindingControls from "./distanceFindingControls"; function MoveControls({ - movedObjects, - setMovedObjects, - itemsGroupRef, - copiedObjects, - setCopiedObjects, - pastedObjects, - setpastedObjects, - duplicatedObjects, - setDuplicatedObjects, - selectionGroup, - rotatedObjects, - setRotatedObjects, - boundingBoxRef, -}: any) { - const { camera, controls, gl, scene, pointer, raycaster } = useThree(); - const plane = useMemo( - () => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), - [] - ); - - const { toggleView } = useToggleView(); - const { selectedAssets, setSelectedAssets } = useSelectedAssets(); - const { selectedProduct } = useSelectedProduct(); - const { floorItems, setFloorItems } = useFloorItems(); - const { socket } = useSocketStore(); - const itemsData = useRef([]); - const [keyEvent, setKeyEvent] = useState< - "Ctrl" | "Shift" | "Ctrl+Shift" | "" - >(""); - const email = localStorage.getItem("email"); - const organization = email?.split("@")[1].split(".")[0] ?? null; - - const updateBackend = ( - productName: string, - productId: string, - organization: string, - eventData: EventsSchema - ) => { - upsertProductOrEventApi({ - productName: productName, - productId: productId, - organization: organization, - eventDatas: eventData, - }); - }; - - useEffect(() => { - if (!camera || !scene || toggleView || !itemsGroupRef.current) return; - - const canvasElement = gl.domElement; - canvasElement.tabIndex = 0; - - let isMoving = false; - - const onPointerDown = () => { - isMoving = false; - }; - - const onPointerMove = () => { - isMoving = true; - }; - const onKeyUp = (event: KeyboardEvent) => { - // When any modifier is released, reset snap - const isModifierKey = event.key === "Control" || event.key === "Shift"; - - if (isModifierKey) { - setKeyEvent(""); - } - }; - - const onPointerUp = (event: PointerEvent) => { - if (!isMoving && movedObjects.length > 0 && event.button === 0) { - event.preventDefault(); - placeMovedAssets(); - } - if (!isMoving && movedObjects.length > 0 && event.button === 2) { - event.preventDefault(); - - clearSelection(); - movedObjects.forEach((asset: any) => { - if (itemsGroupRef.current) { - itemsGroupRef.current.attach(asset); - } - }); - - setFloorItems([...floorItems, ...itemsData.current]); - - setMovedObjects([]); - itemsData.current = []; - } - setKeyEvent(""); - }; - - const onKeyDown = (event: KeyboardEvent) => { - const keyCombination = detectModifierKeys(event); - - if ( - pastedObjects.length > 0 || - duplicatedObjects.length > 0 || - rotatedObjects.length > 0 - ) - return; - - if ( - keyCombination === "Ctrl" || - keyCombination === "Ctrl+Shift" || - keyCombination === "Shift" - ) { - // update state here - setKeyEvent(keyCombination); - } else { - setKeyEvent(""); - } - - if (keyCombination === "G") { - if (selectedAssets.length > 0) { - moveAssets(); - itemsData.current = floorItems.filter((item: { modelUuid: string }) => - selectedAssets.some((asset: any) => asset.uuid === item.modelUuid) - ); - } - } - - if (keyCombination === "ESCAPE") { - event.preventDefault(); - - clearSelection(); - movedObjects.forEach((asset: any) => { - if (itemsGroupRef.current) { - itemsGroupRef.current.attach(asset); - } - }); - - setFloorItems([...floorItems, ...itemsData.current]); - - setMovedObjects([]); - itemsData.current = []; - } - }; - - if (!toggleView) { - canvasElement.addEventListener("pointerdown", onPointerDown); - canvasElement.addEventListener("pointermove", onPointerMove); - canvasElement.addEventListener("pointerup", onPointerUp); - canvasElement.addEventListener("keydown", onKeyDown); - canvasElement?.addEventListener("keyup", onKeyUp); - } - - return () => { - canvasElement.removeEventListener("pointerdown", onPointerDown); - canvasElement.removeEventListener("pointermove", onPointerMove); - canvasElement.removeEventListener("pointerup", onPointerUp); - canvasElement.removeEventListener("keydown", onKeyDown); - canvasElement?.removeEventListener("keyup", onKeyUp); - }; - }, [ - camera, - controls, - scene, - toggleView, - selectedAssets, - socket, - floorItems, - pastedObjects, - duplicatedObjects, movedObjects, + setMovedObjects, + itemsGroupRef, + pastedObjects, + setpastedObjects, + duplicatedObjects, + setDuplicatedObjects, + selectionGroup, rotatedObjects, - keyEvent, - ]); + setRotatedObjects, + boundingBoxRef, +}: any) { + const { camera, controls, gl, scene, pointer, raycaster } = useThree(); + const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); - let moveSpeed = keyEvent === "Ctrl" || "Ctrl+Shift" ? 1 : 0.25; + const { toggleView } = useToggleView(); + const { selectedAssets, setSelectedAssets } = useSelectedAssets(); + const { selectedProduct } = useSelectedProduct(); + const { floorItems, setFloorItems } = useFloorItems(); + const { socket } = useSocketStore(); + const itemsData = useRef([]); + const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">(""); + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; - useFrame(() => { - if (movedObjects.length > 0) { - const intersectionPoint = new THREE.Vector3(); - raycaster.setFromCamera(pointer, camera); - const point = raycaster.ray.intersectPlane(plane, intersectionPoint); + const updateBackend = ( + productName: string, + productId: string, + organization: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productId: productId, + organization: organization, + eventDatas: eventData, + }); + }; - if (point) { - let targetX = point.x; - let targetZ = point.z; - if (keyEvent === "Ctrl") { - targetX = snapControls(targetX, "Ctrl"); - targetZ = snapControls(targetZ, "Ctrl"); - } - // else if (keyEvent === "Ctrl+Shift") { - // targetX = snapControls(targetX, "Ctrl+Shift"); - // targetZ = snapControls(targetZ, "Ctrl+Shift"); - // } else if (keyEvent === "Shift") { - // targetX = snapControls(targetX, "Shift"); - // targetZ = snapControls(targetZ, "Shift"); - // } else { - // } + useEffect(() => { + if (!camera || !scene || toggleView || !itemsGroupRef.current) return; - const position = new THREE.Vector3(); + const canvasElement = gl.domElement; + canvasElement.tabIndex = 0; - if (boundingBoxRef.current) { - boundingBoxRef.current.getWorldPosition(position); - selectionGroup.current.position.lerp( - new THREE.Vector3( - targetX - (position.x - selectionGroup.current.position.x), - selectionGroup.current.position.y, - targetZ - (position.z - selectionGroup.current.position.z) - ), - moveSpeed - ); - } else { - const box = new THREE.Box3(); - movedObjects.forEach((obj: THREE.Object3D) => - box.expandByObject(obj) - ); - const center = new THREE.Vector3(); - box.getCenter(center); + let isMoving = false; - selectionGroup.current.position.lerp( - new THREE.Vector3( - targetX - (center.x - selectionGroup.current.position.x), - selectionGroup.current.position.y, - targetZ - (center.z - selectionGroup.current.position.z) - ), - moveSpeed - ); - } - } - } - }); - - const moveAssets = () => { - const updatedItems = floorItems.filter( - (item: { modelUuid: string }) => - !selectedAssets.some((asset: any) => asset.uuid === item.modelUuid) - ); - setFloorItems(updatedItems); - setMovedObjects(selectedAssets); - selectedAssets.forEach((asset: any) => { - selectionGroup.current.attach(asset); - }); - }; - - const placeMovedAssets = () => { - if (movedObjects.length === 0) return; - - movedObjects.forEach(async (obj: THREE.Object3D) => { - const worldPosition = new THREE.Vector3(); - obj.getWorldPosition(worldPosition); - - selectionGroup.current.remove(obj); - obj.position.copy(worldPosition); - - if (itemsGroupRef.current) { - const newFloorItem: Types.FloorItemType = { - modelUuid: obj.uuid, - modelName: obj.userData.name, - modelfileID: obj.userData.modelId, - position: [worldPosition.x, worldPosition.y, worldPosition.z], - rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, - isLocked: false, - isVisible: true, + const onPointerDown = () => { + isMoving = false; }; - if (obj.userData.eventData) { - const eventData = useEventsStore - .getState() - .getEventByModelUuid(obj.userData.modelUuid); - const productData = useProductStore - .getState() - .getEventByModelUuid( - useSelectedProduct.getState().selectedProduct.productId, - obj.userData.modelUuid - ); + const onPointerMove = () => { + isMoving = true; + }; - if (eventData) { - useEventsStore.getState().updateEvent(obj.userData.modelUuid, { - position: [worldPosition.x, worldPosition.y, worldPosition.z], - rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], - }); - } - if (productData) { - const event = useProductStore - .getState() - .updateEvent( - useSelectedProduct.getState().selectedProduct.productId, - obj.userData.modelUuid, - { - position: [worldPosition.x, worldPosition.y, worldPosition.z], - rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], - } - ); + const onKeyUp = (event: KeyboardEvent) => { + const isModifierKey = event.key === "Control" || event.key === "Shift"; - if (event && organization) { - updateBackend( - selectedProduct.productName, - selectedProduct.productId, - organization, - event - ); + if (isModifierKey) { + setKeyEvent(""); + } + }; + + const onPointerUp = (event: PointerEvent) => { + if (!isMoving && movedObjects.length > 0 && event.button === 0) { + event.preventDefault(); + placeMovedAssets(); + } + if (!isMoving && movedObjects.length > 0 && event.button === 2) { + event.preventDefault(); + + clearSelection(); + movedObjects.forEach((asset: any) => { + if (itemsGroupRef.current) { + itemsGroupRef.current.attach(asset); + } + }); + + setFloorItems([...floorItems, ...itemsData.current]); + + setMovedObjects([]); + itemsData.current = []; + } + setKeyEvent(""); + }; + + const onKeyDown = (event: KeyboardEvent) => { + const keyCombination = detectModifierKeys(event); + + if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || rotatedObjects.length > 0) + return; + + if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") { + setKeyEvent(keyCombination); + } else { + setKeyEvent(""); } - newFloorItem.eventData = eventData; - } - } + if (keyCombination === "G") { + if (selectedAssets.length > 0) { + moveAssets(); + itemsData.current = floorItems.filter((item: { modelUuid: string }) => + selectedAssets.some((asset: any) => asset.uuid === item.modelUuid) + ); + } + } - setFloorItems((prevItems: Types.FloorItems) => { - const updatedItems = [...(prevItems || []), newFloorItem]; - localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); - return updatedItems; - }); + if (keyCombination === "ESCAPE") { + event.preventDefault(); - //REST + clearSelection(); + movedObjects.forEach((asset: any) => { + if (itemsGroupRef.current) { + itemsGroupRef.current.attach(asset); + } + }); - // await setFloorItemApi( - // organization, - // obj.uuid, - // obj.userData.name, - // [worldPosition.x, worldPosition.y, worldPosition.z], - // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, - // obj.userData.modelId, - // false, - // true, - // ); + setFloorItems([...floorItems, ...itemsData.current]); - //SOCKET - - const data = { - organization, - modelUuid: newFloorItem.modelUuid, - modelName: newFloorItem.modelName, - modelfileID: newFloorItem.modelfileID, - position: newFloorItem.position, - rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, - isLocked: false, - isVisible: true, - socketId: socket.id, + setMovedObjects([]); + itemsData.current = []; + } }; - socket.emit("v2:model-asset:add", data); + if (!toggleView) { + canvasElement.addEventListener("pointerdown", onPointerDown); + canvasElement.addEventListener("pointermove", onPointerMove); + canvasElement.addEventListener("pointerup", onPointerUp); + canvasElement.addEventListener("keydown", onKeyDown); + canvasElement?.addEventListener("keyup", onKeyUp); + } - itemsGroupRef.current.add(obj); - } + return () => { + canvasElement.removeEventListener("pointerdown", onPointerDown); + canvasElement.removeEventListener("pointermove", onPointerMove); + canvasElement.removeEventListener("pointerup", onPointerUp); + canvasElement.removeEventListener("keydown", onKeyDown); + canvasElement?.removeEventListener("keyup", onKeyUp); + }; + }, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent,]); + + let moveSpeed = keyEvent === "Ctrl" || "Ctrl+Shift" ? 1 : 0.25; + + useFrame(() => { + if (movedObjects.length > 0) { + const intersectionPoint = new THREE.Vector3(); + raycaster.setFromCamera(pointer, camera); + const point = raycaster.ray.intersectPlane(plane, intersectionPoint); + + if (point) { + let targetX = point.x; + let targetZ = point.z; + + if (keyEvent === "Ctrl") { + targetX = snapControls(targetX, "Ctrl"); + targetZ = snapControls(targetZ, "Ctrl"); + } + + // else if (keyEvent === "Ctrl+Shift") { + // targetX = snapControls(targetX, "Ctrl+Shift"); + // targetZ = snapControls(targetZ, "Ctrl+Shift"); + // } else if (keyEvent === "Shift") { + // targetX = snapControls(targetX, "Shift"); + // targetZ = snapControls(targetZ, "Shift"); + // } else { + // } + + const position = new THREE.Vector3(); + + if (boundingBoxRef.current) { + boundingBoxRef.current.getWorldPosition(position); + selectionGroup.current.position.lerp( + new THREE.Vector3( + targetX - (position.x - selectionGroup.current.position.x), + selectionGroup.current.position.y, + targetZ - (position.z - selectionGroup.current.position.z) + ), + moveSpeed + ); + } else { + const box = new THREE.Box3(); + movedObjects.forEach((obj: THREE.Object3D) => + box.expandByObject(obj) + ); + const center = new THREE.Vector3(); + box.getCenter(center); + + selectionGroup.current.position.lerp( + new THREE.Vector3( + targetX - (center.x - selectionGroup.current.position.x), + selectionGroup.current.position.y, + targetZ - (center.z - selectionGroup.current.position.z) + ), + moveSpeed + ); + } + } + } }); - toast.success("Object moved!"); - itemsData.current = []; - clearSelection(); - }; + const moveAssets = () => { + const updatedItems = floorItems.filter( + (item: { modelUuid: string }) => + !selectedAssets.some((asset: any) => asset.uuid === item.modelUuid) + ); + setFloorItems(updatedItems); + setMovedObjects(selectedAssets); + selectedAssets.forEach((asset: any) => { + selectionGroup.current.attach(asset); + }); + }; - const clearSelection = () => { - selectionGroup.current.children = []; - selectionGroup.current.position.set(0, 0, 0); - selectionGroup.current.rotation.set(0, 0, 0); - setpastedObjects([]); - setDuplicatedObjects([]); - setMovedObjects([]); - setRotatedObjects([]); - setSelectedAssets([]); - setKeyEvent(""); - }; + const placeMovedAssets = () => { + if (movedObjects.length === 0) return; - return ( - - ); + movedObjects.forEach(async (obj: THREE.Object3D) => { + const worldPosition = new THREE.Vector3(); + obj.getWorldPosition(worldPosition); + + selectionGroup.current.remove(obj); + obj.position.copy(worldPosition); + + if (itemsGroupRef.current) { + const newFloorItem: Types.FloorItemType = { + modelUuid: obj.uuid, + modelName: obj.userData.name, + modelfileID: obj.userData.modelId, + position: [worldPosition.x, worldPosition.y, worldPosition.z], + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, + isLocked: false, + isVisible: true, + }; + + if (obj.userData.eventData) { + const eventData = useEventsStore.getState().getEventByModelUuid(obj.userData.modelUuid); + const productData = useProductStore.getState().getEventByModelUuid(selectedProduct.productId, obj.userData.modelUuid); + + if (eventData) { + useEventsStore.getState().updateEvent(obj.userData.modelUuid, { + position: [worldPosition.x, worldPosition.y, worldPosition.z], + rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], + }); + } + + if (productData) { + const event = useProductStore + .getState() + .updateEvent( + selectedProduct.productId, + obj.userData.modelUuid, + { + position: [worldPosition.x, worldPosition.y, worldPosition.z], + rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], + } + ); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } + + newFloorItem.eventData = eventData; + } + } + + setFloorItems((prevItems: Types.FloorItems) => { + const updatedItems = [...(prevItems || []), newFloorItem]; + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; + }); + + //REST + + // await setFloorItemApi( + // organization, + // obj.uuid, + // obj.userData.name, + // [worldPosition.x, worldPosition.y, worldPosition.z], + // { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z }, + // obj.userData.modelId, + // false, + // true, + // ); + + //SOCKET + + const data = { + organization, + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, + modelfileID: newFloorItem.modelfileID, + position: newFloorItem.position, + rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, + isLocked: false, + isVisible: true, + socketId: socket.id, + }; + + socket.emit("v2:model-asset:add", data); + + itemsGroupRef.current.add(obj); + } + }); + echo.success("Object moved!"); + + itemsData.current = []; + clearSelection(); + }; + + const clearSelection = () => { + selectionGroup.current.children = []; + selectionGroup.current.position.set(0, 0, 0); + selectionGroup.current.rotation.set(0, 0, 0); + setpastedObjects([]); + setDuplicatedObjects([]); + setMovedObjects([]); + setRotatedObjects([]); + setSelectedAssets([]); + setKeyEvent(""); + }; + + return ( + + ); } export default MoveControls; diff --git a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx index 31ba8c9..03b262c 100644 --- a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx @@ -1,9 +1,8 @@ import * as THREE from "three"; import { useEffect, useMemo, useRef } from "react"; import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/store"; +import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/builder/store"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; -import { toast } from "react-toastify"; import * as Types from "../../../../types/world/worldTypes"; import { useEventsStore } from "../../../../store/simulation/useEventsStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; @@ -265,7 +264,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo itemsGroupRef.current.add(obj); } }); - toast.success("Object rotated!"); + echo.success("Object rotated!"); itemsData.current = []; clearSelection(); diff --git a/app/src/modules/scene/controls/selectionControls/selectionControls.tsx b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx index 5041434..16f992b 100644 --- a/app/src/modules/scene/controls/selectionControls/selectionControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/selectionControls.tsx @@ -3,9 +3,8 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { SelectionBox } from "three/examples/jsm/interactive/SelectionBox"; import { SelectionHelper } from "./selectionHelper"; import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView, } from "../../../../store/store"; +import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView, } from "../../../../store/builder/store"; import BoundingBox from "./boundingBoxHelper"; -import { toast } from "react-toastify"; // import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; @@ -51,7 +50,7 @@ const SelectionControls: React.FC = () => { const helper = new SelectionHelper(gl); if (!itemsGroup) { - toast.warn("itemsGroup not found in the scene."); + echo.warn("itemsGroup not found in the scene."); return; } @@ -251,7 +250,7 @@ const SelectionControls: React.FC = () => { const updatedItems = floorItems.filter((item: { modelUuid: string }) => !selectedUUIDs.includes(item.modelUuid)); setFloorItems(updatedItems); } - toast.success("Selected models removed!"); + echo.success("Selected models removed!"); clearSelection(); }; diff --git a/app/src/modules/scene/controls/transformControls/transformControls.tsx b/app/src/modules/scene/controls/transformControls/transformControls.tsx new file mode 100644 index 0000000..1108cf2 --- /dev/null +++ b/app/src/modules/scene/controls/transformControls/transformControls.tsx @@ -0,0 +1,212 @@ +import { TransformControls } from "@react-three/drei"; +import * as THREE from "three"; +import { useSelectedFloorItem, useObjectPosition, useObjectRotation, useFloorItems, useActiveTool, useSocketStore } from "../../../../store/builder/store"; +import { useThree } from "@react-three/fiber"; + +import * as Types from '../../../../types/world/worldTypes'; +import { useEffect, useState } from "react"; +import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; +import { useEventsStore } from "../../../../store/simulation/useEventsStore"; +import { useProductStore } from "../../../../store/simulation/useProductStore"; +import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; +import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi"; +import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; + +export default function TransformControl() { + const state = useThree(); + const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null); + const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem(); + const { setObjectPosition } = useObjectPosition(); + const { setObjectRotation } = useObjectRotation(); + const { setFloorItems } = useFloorItems(); + const { activeTool } = useActiveTool(); + const { socket } = useSocketStore(); + const { selectedProduct } = useSelectedProduct(); + + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + + const updateBackend = ( + productName: string, + productId: string, + organization: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productId: productId, + organization: organization, + eventDatas: eventData, + }); + }; + + function handleObjectChange() { + if (selectedFloorItem) { + setObjectPosition(selectedFloorItem.position); + setObjectRotation({ + x: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.x), + y: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.y), + z: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.z), + }); + } + } + + function handleMouseUp() { + if (selectedFloorItem) { + setObjectPosition(selectedFloorItem.position); + setObjectRotation({ + x: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.x), + y: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.y), + z: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.z), + }); + } + setFloorItems((prevItems: Types.FloorItems) => { + if (!prevItems) { + return + } + let updatedItem: any = null; + const updatedItems = prevItems.map((item) => { + if (item.modelUuid === selectedFloorItem?.uuid) { + updatedItem = { + ...item, + position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z] as [number, number, number], + rotation: { x: selectedFloorItem.rotation.x, y: selectedFloorItem.rotation.y, z: selectedFloorItem.rotation.z }, + }; + return updatedItem; + } + return item; + }); + if (updatedItem && selectedFloorItem) { + if (updatedItem.eventData) { + const eventData = useEventsStore.getState().getEventByModelUuid(updatedItem.modelUuid); + const productData = useProductStore.getState().getEventByModelUuid(selectedProduct.productId, updatedItem.modelUuid); + + if (eventData) { + useEventsStore.getState().updateEvent(updatedItem.modelUuid, { + position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z], + rotation: [selectedFloorItem.rotation.x, selectedFloorItem.rotation.y, selectedFloorItem.rotation.z], + }); + } + + if (productData) { + const event = useProductStore + .getState() + .updateEvent( + selectedProduct.productId, + updatedItem.modelUuid, + { + position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z], + rotation: [selectedFloorItem.rotation.x, selectedFloorItem.rotation.y, selectedFloorItem.rotation.z], + } + ); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } + + updatedItem.eventData = eventData; + } + } + + setFloorItems((prevItems: Types.FloorItems) => { + const updatedItems = [...(prevItems || []), updatedItem]; + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; + }); + + // REST + + // setFloorItemApi( + // organization, + // updatedItem.modelUuid, + // updatedItem.modelName, + // updatedItem.modelfileid, + // [selectedFloorItem.position.x, 0, selectedFloorItem.position.z,], + // { "x": selectedFloorItem.rotation.x, "y": selectedFloorItem.rotation.y, "z": selectedFloorItem.rotation.z }, + // false, + // true, + // ); + + // SOCKET + + const data = { + organization: organization, + modelUuid: updatedItem.modelUuid, + modelName: updatedItem.modelName, + modelfileID: updatedItem.modelfileID, + position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z], + rotation: { "x": selectedFloorItem.rotation.x, "y": selectedFloorItem.rotation.y, "z": selectedFloorItem.rotation.z }, + isLocked: false, + isVisible: true, + socketId: socket.id + } + + socket.emit("v2:model-asset:add", data); + } + localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); + return updatedItems; + }); + } + + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + const keyCombination = detectModifierKeys(e); + if (!selectedFloorItem) return; + if (keyCombination === "G") { + setTransformMode((prev) => (prev === "translate" ? null : "translate")); + } + if (keyCombination === "R") { + setTransformMode((prev) => (prev === "rotate" ? null : "rotate")); + } + }; + + if (selectedFloorItem) { + window.addEventListener("keydown", handleKeyDown); + setObjectPosition(selectedFloorItem.position); + setObjectRotation({ + x: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.x), + y: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.y), + z: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.z), + }); + } else { + setTransformMode(null); + } + + return () => { + window.removeEventListener("keydown", handleKeyDown); + }; + }, [selectedFloorItem]); + + useEffect(() => { + if (activeTool === "delete") { + if (state.controls) { + const target = (state.controls as any).getTarget(new THREE.Vector3()); + (state.controls as any).setTarget(target.x, 0, target.z, true); + } + setSelectedFloorItem(null); + setObjectPosition({ x: undefined, y: undefined, z: undefined }); + setObjectRotation({ x: undefined, y: undefined, z: undefined }); + } + }, [activeTool]); + + return ( + <> + {(selectedFloorItem && transformMode) && + + } + + ); +} diff --git a/app/src/modules/scene/environment/ground.tsx b/app/src/modules/scene/environment/ground.tsx index ef3e690..e8d36d1 100644 --- a/app/src/modules/scene/environment/ground.tsx +++ b/app/src/modules/scene/environment/ground.tsx @@ -1,4 +1,4 @@ -import { useTileDistance, useToggleView } from "../../../store/store"; +import { useTileDistance, useToggleView } from "../../../store/builder/store"; import * as CONSTANTS from "../../../types/world/worldConstants"; const Ground = ({ grid, plane }: any) => { diff --git a/app/src/modules/scene/environment/shadow.tsx b/app/src/modules/scene/environment/shadow.tsx index 4625cde..e44b9cd 100644 --- a/app/src/modules/scene/environment/shadow.tsx +++ b/app/src/modules/scene/environment/shadow.tsx @@ -9,7 +9,7 @@ import { useFloorItems, useWallItems, useTileDistance, -} from "../../../store/store"; +} from "../../../store/builder/store"; import * as CONSTANTS from "../../../types/world/worldConstants"; const shadowWorker = new Worker( new URL( diff --git a/app/src/modules/scene/environment/sky.tsx b/app/src/modules/scene/environment/sky.tsx index dbfda22..4a584ee 100644 --- a/app/src/modules/scene/environment/sky.tsx +++ b/app/src/modules/scene/environment/sky.tsx @@ -1,6 +1,6 @@ import * as THREE from 'three'; import { Sky } from "@react-three/drei"; -import { useAzimuth, useElevation, useSunPosition } from "../../../store/store"; +import { useAzimuth, useElevation, useSunPosition } from "../../../store/builder/store"; import { useEffect, useRef, useState } from "react"; import * as CONSTANTS from '../../../types/world/worldConstants'; diff --git a/app/src/modules/scene/postProcessing/postProcessing.tsx b/app/src/modules/scene/postProcessing/postProcessing.tsx index dd5607a..426e199 100644 --- a/app/src/modules/scene/postProcessing/postProcessing.tsx +++ b/app/src/modules/scene/postProcessing/postProcessing.tsx @@ -5,7 +5,7 @@ import { useDeletableFloorItem, useSelectedWallItem, useSelectedFloorItem, -} from "../../../store/store"; +} from "../../../store/builder/store"; import * as Types from "../../../types/world/worldTypes"; import * as CONSTANTS from "../../../types/world/worldConstants"; import { useEffect } from "react"; @@ -57,9 +57,7 @@ export default function PostProcessing() { )} {selectedWallItem && ( child.name !== "CSG_REF" - )} + selection={flattenChildren(selectedWallItem.children)} selectionLayer={10} width={3000} blendFunction={BlendFunction.ALPHA} diff --git a/app/src/modules/scene/tools/measurementTool.tsx b/app/src/modules/scene/tools/measurementTool.tsx index 9a30da3..1a1c972 100644 --- a/app/src/modules/scene/tools/measurementTool.tsx +++ b/app/src/modules/scene/tools/measurementTool.tsx @@ -1,7 +1,7 @@ import * as THREE from "three"; import { useEffect, useRef, useState } from "react"; import { useThree, useFrame } from "@react-three/fiber"; -import { useToolMode } from "../../../store/store"; +import { useToolMode } from "../../../store/builder/store"; import { Html } from "@react-three/drei"; const MeasurementTool = () => { diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/useDespawnHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useDespawnHandler.ts index 9364d70..48cc3e7 100644 --- a/app/src/modules/simulation/actions/conveyor/actionHandler/useDespawnHandler.ts +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/useDespawnHandler.ts @@ -2,7 +2,7 @@ import { useCallback } from "react"; import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore"; export function useDespawnHandler() { - const { getMaterialById, removeMaterial } = useMaterialStore(); + const { getMaterialById, removeMaterial, setEndTime } = useMaterialStore(); const deSpawnLogStatus = (materialUuid: string, status: string) => { echo.info(`${materialUuid}, ${status}`); @@ -14,6 +14,7 @@ export function useDespawnHandler() { const material = getMaterialById(materialId); if (!material) return; + setEndTime(material.materialId, performance.now()); removeMaterial(material.materialId); deSpawnLogStatus(material.materialName, `Despawned`); diff --git a/app/src/modules/simulation/actions/storageUnit/actionHandler/useStoreHandler.ts b/app/src/modules/simulation/actions/storageUnit/actionHandler/useStoreHandler.ts index 2b0e990..0e8a920 100644 --- a/app/src/modules/simulation/actions/storageUnit/actionHandler/useStoreHandler.ts +++ b/app/src/modules/simulation/actions/storageUnit/actionHandler/useStoreHandler.ts @@ -5,7 +5,7 @@ import { useProductStore } from "../../../../../store/simulation/useProductStore import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; export function useStoreHandler() { - const { getMaterialById, removeMaterial } = useMaterialStore(); + const { getMaterialById, removeMaterial, setEndTime } = useMaterialStore(); const { addCurrentMaterial, updateCurrentLoad } = useStorageUnitStore(); const { getModelUuidByActionUuid } = useProductStore(); const { selectedProduct } = useSelectedProduct(); @@ -23,6 +23,7 @@ export function useStoreHandler() { const modelUuid = getModelUuidByActionUuid(selectedProduct.productId, action.actionUuid); if (!modelUuid) return; + setEndTime(material.materialId, performance.now()); removeMaterial(material.materialId); addCurrentMaterial(modelUuid, material.materialType, material.materialId); updateCurrentLoad(modelUuid, 1); diff --git a/app/src/modules/simulation/simulator/simulator.tsx b/app/src/modules/simulation/simulator/simulator.tsx index d6ff2fa..aa0b9f6 100644 --- a/app/src/modules/simulation/simulator/simulator.tsx +++ b/app/src/modules/simulation/simulator/simulator.tsx @@ -3,22 +3,27 @@ import { useProductStore } from '../../../store/simulation/useProductStore'; import { useActionHandler } from '../actions/useActionHandler'; import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayButtonStore'; import { determineExecutionOrder } from './functions/determineExecutionOrder'; +import { useSelectedProduct } from '../../../store/simulation/useSimulationStore'; function Simulator() { - const { products } = useProductStore(); + const { products, getProductById } = useProductStore(); const { handleAction } = useActionHandler(); + const { selectedProduct } = useSelectedProduct(); const { isPlaying } = usePlayButtonStore(); const { isReset } = useResetButtonStore(); useEffect(() => { - if (!isPlaying || isReset) return; + if (!isPlaying || isReset || !selectedProduct.productId) return; - const executionOrder = determineExecutionOrder(products); + const product = getProductById(selectedProduct.productId); + if (!product) return; + + const executionOrder = determineExecutionOrder([product]); executionOrder.forEach(point => { const action = 'actions' in point ? point.actions[0] : point.action; handleAction(action); }); - }, [products, isPlaying, isReset]); + }, [products, isPlaying, isReset, selectedProduct]); return ( diff --git a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx index 557831c..dc8fb11 100644 --- a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx +++ b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx @@ -9,7 +9,7 @@ import { useSelectedProduct } from "../../../../store/simulation/useSimulationSt import { handleAddEventToProduct } from "../../events/points/functions/handleAddEventToProduct"; import { QuadraticBezierLine } from "@react-three/drei"; import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi"; -import { useDeleteTool } from "../../../../store/store"; +import { useDeleteTool } from "../../../../store/builder/store"; import { usePlayButtonStore } from "../../../../store/usePlayButtonStore"; interface ConnectionLine { diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx index a3af481..9d51a8d 100644 --- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx +++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx @@ -64,7 +64,91 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai } }, [isReset, isPlaying]) - useFrame((_, delta) => { + // useFrame((_, delta) => { + // const object = scene.getObjectByProperty('uuid', agvUuid); + // if (!object || currentPath.length < 2) return; + // if (isPaused) return; + + // let totalDistance = 0; + // const distances = []; + // let accumulatedDistance = 0; + // let index = 0; + // const rotationSpeed = 1; + + // for (let i = 0; i < currentPath.length - 1; i++) { + // const start = new THREE.Vector3(...currentPath[i]); + // const end = new THREE.Vector3(...currentPath[i + 1]); + // const segmentDistance = start.distanceTo(end); + // distances.push(segmentDistance); + // totalDistance += segmentDistance; + // } + + // while (index < distances.length && progressRef.current > accumulatedDistance + distances[index]) { + // accumulatedDistance += distances[index]; + // index++; + // } + + // if (index < distances.length) { + // const start = new THREE.Vector3(...currentPath[index]); + // const end = new THREE.Vector3(...currentPath[index + 1]); + // const segmentDistance = distances[index]; + + // const currentDirection = new THREE.Vector3().subVectors(end, start).normalize(); + // const targetAngle = Math.atan2(currentDirection.x, currentDirection.z); + + // const currentAngle = object.rotation.y; + + // let angleDifference = targetAngle - currentAngle; + // if (angleDifference > Math.PI) angleDifference -= 2 * Math.PI; + // if (angleDifference < -Math.PI) angleDifference += 2 * Math.PI; + + // const maxRotationStep = (rotationSpeed * speed * agvDetail.speed) * delta; + // object.rotation.y += Math.sign(angleDifference) * Math.min(Math.abs(angleDifference), maxRotationStep); + // const isAligned = Math.abs(angleDifference) < 0.01; + + // if (isAligned) { + // progressRef.current += delta * (speed * agvDetail.speed); + // const t = (progressRef.current - accumulatedDistance) / segmentDistance; + // const position = start.clone().lerp(end, t); + // object.position.copy(position); + // } + // } + + // if (progressRef.current >= totalDistance) { + // if (restRotation && objectRotation) { + // const targetEuler = new THREE.Euler( + // objectRotation.x, + // objectRotation.y - (agvDetail.point.action.steeringAngle), + // objectRotation.z + // ); + // const targetQuaternion = new THREE.Quaternion().setFromEuler(targetEuler); + // object.quaternion.slerp(targetQuaternion, delta * (rotationSpeed * speed * agvDetail.speed)); + // if (object.quaternion.angleTo(targetQuaternion) < 0.01) { + // object.quaternion.copy(targetQuaternion); + // object.rotation.copy(targetEuler); + // setRestingRotation(false); + // } + // return; + // } + // } + // if (progressRef.current >= totalDistance) { + // setRestingRotation(true); + // progressRef.current = 0; + // movingForward.current = !movingForward.current; + // setCurrentPath([]); + // handleCallBack(); + // if (currentPhase === 'pickup-drop') { + // requestAnimationFrame(startUnloadingProcess); + // } + // } + // }); + const lastTimeRef = useRef(performance.now()); + + useFrame(() => { + const now = performance.now(); + const delta = (now - lastTimeRef.current) / 1000; + lastTimeRef.current = now; + const object = scene.getObjectByProperty('uuid', agvUuid); if (!object || currentPath.length < 2) return; if (isPaused) return; @@ -95,7 +179,6 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai const currentDirection = new THREE.Vector3().subVectors(end, start).normalize(); const targetAngle = Math.atan2(currentDirection.x, currentDirection.z); - const currentAngle = object.rotation.y; let angleDifference = targetAngle - currentAngle; @@ -118,7 +201,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai if (restRotation && objectRotation) { const targetEuler = new THREE.Euler( objectRotation.x, - objectRotation.y - (agvDetail.point.action.steeringAngle), + objectRotation.y - agvDetail.point.action.steeringAngle, objectRotation.z ); const targetQuaternion = new THREE.Quaternion().setFromEuler(targetEuler); @@ -131,6 +214,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai return; } } + if (progressRef.current >= totalDistance) { setRestingRotation(true); progressRef.current = 0; diff --git a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx index f4e7e61..4031713 100644 --- a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx +++ b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; import VehicleAnimator from '../animator/vehicleAnimator'; import * as THREE from 'three'; import { NavMeshQuery } from '@recast-navigation/core'; -import { useNavMesh } from '../../../../../store/store'; +import { useNavMesh } from '../../../../../store/builder/store'; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore'; import { useStorageUnitStore } from '../../../../../store/simulation/useStorageUnitStore'; @@ -11,11 +11,14 @@ import { useProductStore } from '../../../../../store/simulation/useProductStore import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore'; import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler'; import MaterialAnimator from '../animator/materialAnimator'; - +type Timer = { + start: number | null; + active: boolean; +}; function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) { const { navMesh } = useNavMesh(); const { isPlaying } = usePlayButtonStore(); - const { removeMaterial } = useMaterialStore(); + const { removeMaterial, setEndTime } = useMaterialStore(); const { getStorageUnitById } = useStorageUnitStore(); const { triggerPointActions } = useTriggerHandler(); const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = useProductStore(); @@ -51,7 +54,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) ); function vehicleStatus(modelId: string, status: string) { - // console.log(`${modelId} , ${status}`); + // } // Function to reset everything @@ -144,6 +147,9 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) } } + + + function startUnloadingProcess() { if (agvDetail.point.action.triggers.length > 0) { const trigger = getTriggerByUuid(selectedProduct.productId, agvDetail.point.action.triggers[0].triggerUuid); @@ -296,6 +302,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) decrementVehicleLoad(agvDetail.modelUuid, 1); const material = removeLastMaterial(agvDetail.modelUuid); if (material) { + setEndTime(material.materialId, performance.now()); removeMaterial(material.materialId); } if (droppedMat > 0) { diff --git a/app/src/modules/simulation/vehicle/navMesh/navMesh.tsx b/app/src/modules/simulation/vehicle/navMesh/navMesh.tsx index 8652172..2dcb9b2 100644 --- a/app/src/modules/simulation/vehicle/navMesh/navMesh.tsx +++ b/app/src/modules/simulation/vehicle/navMesh/navMesh.tsx @@ -1,5 +1,5 @@ import { useRef } from "react"; -import { useNavMesh } from "../../../../store/store"; +import { useNavMesh } from "../../../../store/builder/store"; import PolygonGenerator from "./polygonGenerator"; import NavMeshDetails from "./navMeshDetails"; import * as CONSTANTS from "../../../../types/world/worldConstants"; diff --git a/app/src/modules/visualization/mqttTemp/drieHtmlTemp.tsx b/app/src/modules/visualization/mqttTemp/drieHtmlTemp.tsx index 3e0adba..92631d6 100644 --- a/app/src/modules/visualization/mqttTemp/drieHtmlTemp.tsx +++ b/app/src/modules/visualization/mqttTemp/drieHtmlTemp.tsx @@ -1,7 +1,7 @@ import { Html } from "@react-three/drei"; import * as THREE from "three"; import * as Types from "../../../types/world/worldTypes"; -import { useDrieTemp, useDrieUIValue } from "../../../store/store" +import { useDrieTemp, useDrieUIValue } from "../../../store/builder/store" import UI from "./ui"; import { useEffect } from "react"; import { useThree } from "@react-three/fiber"; diff --git a/app/src/modules/visualization/socket/realTimeVizSocket.dev.tsx b/app/src/modules/visualization/socket/realTimeVizSocket.dev.tsx index 1dbc175..63d634f 100644 --- a/app/src/modules/visualization/socket/realTimeVizSocket.dev.tsx +++ b/app/src/modules/visualization/socket/realTimeVizSocket.dev.tsx @@ -1,5 +1,5 @@ import { useEffect } from "react"; -import { useSocketStore } from "../../../store/store"; +import { useSocketStore } from "../../../store/builder/store"; import { useSelectedZoneStore } from "../../../store/visualization/useZoneStore"; import { useDroppedObjectsStore } from "../../../store/visualization/useDroppedObjectsStore"; import { useZoneWidgetStore } from "../../../store/visualization/useZone3DWidgetStore"; diff --git a/app/src/modules/visualization/template/Templates.tsx b/app/src/modules/visualization/template/Templates.tsx index 0a79120..d7f6b3f 100644 --- a/app/src/modules/visualization/template/Templates.tsx +++ b/app/src/modules/visualization/template/Templates.tsx @@ -1,7 +1,7 @@ import { useEffect } from "react"; import useTemplateStore from "../../../store/useTemplateStore"; import { useSelectedZoneStore } from "../../../store/visualization/useZoneStore"; -import { useSocketStore } from "../../../store/store"; +import { useSocketStore } from "../../../store/builder/store"; import { getTemplateData } from "../../../services/visulization/zone/getTemplate"; import { useDroppedObjectsStore } from "../../../store/visualization/useDroppedObjectsStore"; import RenameInput from "../../../components/ui/inputs/RenameInput"; diff --git a/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx b/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx index 0b4f7c8..aa1c635 100644 --- a/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx +++ b/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx @@ -14,7 +14,7 @@ import { } from "../../../../components/icons/ExportCommonIcons"; import { useEffect, useRef, useState } from "react"; import { useClickOutside } from "../../functions/handleWidgetsOuterClick"; -import { useSocketStore } from "../../../../store/store"; +import { useSocketStore } from "../../../../store/builder/store"; import { usePlayButtonStore } from "../../../../store/usePlayButtonStore"; import OuterClick from "../../../../utils/outerClick"; import useChartStore from "../../../../store/visualization/useChartStore"; diff --git a/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx b/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx index 15d63b1..dd0b3fc 100644 --- a/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx +++ b/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx @@ -5,7 +5,7 @@ import { useAsset3dWidget, useSocketStore, useWidgetSubOption, -} from "../../../../store/store"; +} from "../../../../store/builder/store"; import useModuleStore from "../../../../store/useModuleStore"; import { ThreeState } from "../../../../types/world/worldTypes"; import { useSelectedZoneStore } from "../../../../store/visualization/useZoneStore"; diff --git a/app/src/modules/visualization/widgets/floating/DroppedFloatingWidgets.tsx b/app/src/modules/visualization/widgets/floating/DroppedFloatingWidgets.tsx index 14bba3c..409b9ad 100644 --- a/app/src/modules/visualization/widgets/floating/DroppedFloatingWidgets.tsx +++ b/app/src/modules/visualization/widgets/floating/DroppedFloatingWidgets.tsx @@ -15,7 +15,7 @@ import TotalCardComponent from "./cards/TotalCardComponent"; import WarehouseThroughputComponent from "./cards/WarehouseThroughputComponent"; import FleetEfficiencyComponent from "./cards/FleetEfficiencyComponent"; import { useWidgetStore } from "../../../../store/useWidgetStore"; -import { useSocketStore } from "../../../../store/store"; +import { useSocketStore } from "../../../../store/builder/store"; import { usePlayButtonStore } from "../../../../store/usePlayButtonStore"; import { useSelectedZoneStore } from "../../../../store/visualization/useZoneStore"; interface DraggingState { @@ -478,6 +478,7 @@ const DroppedObjects: React.FC = () => { const widthMultiplier = parseFloat(containerWidth) * 0.13; + console.log('zone?.objects: ', zone?.objects); return (
    = ({ className: event.currentTarget.className, position: [rect.top, rect.left], // ✅ Store position }); - + event.dataTransfer.setData("text/plain", cardData); - console.log("cardData: ", cardData); + console.log('cardData: ', cardData); }; return ( diff --git a/app/src/modules/visualization/widgets/floating/cards/TotalCardComponent.tsx b/app/src/modules/visualization/widgets/floating/cards/TotalCardComponent.tsx index 72825e7..edc19e6 100644 --- a/app/src/modules/visualization/widgets/floating/cards/TotalCardComponent.tsx +++ b/app/src/modules/visualization/widgets/floating/cards/TotalCardComponent.tsx @@ -12,10 +12,12 @@ import { } from "../../../../../components/icons/3dChartIcons"; const TotalCardComponent = ({ object }: any) => { + console.log('object: ', object); const [progress, setProgress] = useState(0); const [measurements, setmeasurements] = useState({}); const [duration, setDuration] = useState("1h"); const [name, setName] = useState(object.header ? object.header : ""); + console.log('name: ', name); const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; const { header, flotingDuration, flotingMeasurements } = useChartStore(); diff --git a/app/src/modules/visualization/widgets/panel/AddButtons.tsx b/app/src/modules/visualization/widgets/panel/AddButtons.tsx index 58d2631..53cbca5 100644 --- a/app/src/modules/visualization/widgets/panel/AddButtons.tsx +++ b/app/src/modules/visualization/widgets/panel/AddButtons.tsx @@ -5,7 +5,7 @@ import { LockIcon, } from "../../../../components/icons/RealTimeVisulationIcons"; import { AddIcon } from "../../../../components/icons/ExportCommonIcons"; -import { useSocketStore } from "../../../../store/store"; +import { useSocketStore } from "../../../../store/builder/store"; // Define the type for `Side` type Side = "top" | "bottom" | "left" | "right"; diff --git a/app/src/modules/visualization/widgets/panel/Panel.tsx b/app/src/modules/visualization/widgets/panel/Panel.tsx index c1eeefc..f0fb86b 100644 --- a/app/src/modules/visualization/widgets/panel/Panel.tsx +++ b/app/src/modules/visualization/widgets/panel/Panel.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useMemo, useRef, useState } from "react"; import { arrayMove } from "@dnd-kit/sortable"; -import { useAsset3dWidget, useSocketStore } from "../../../../store/store"; +import { useAsset3dWidget, useSocketStore } from "../../../../store/builder/store"; import { usePlayButtonStore } from "../../../../store/usePlayButtonStore"; import { useWidgetStore } from "../../../../store/useWidgetStore"; import { DraggableWidget } from "../2d/DraggableWidget"; diff --git a/app/src/modules/visualization/zone/zoneAssets.tsx b/app/src/modules/visualization/zone/zoneAssets.tsx index 309fed4..a5fed54 100644 --- a/app/src/modules/visualization/zone/zoneAssets.tsx +++ b/app/src/modules/visualization/zone/zoneAssets.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef } from 'react' -import { useSelectedFloorItem, useZoneAssetId } from '../../../store/store'; +import { useSelectedFloorItem, useZoneAssetId } from '../../../store/builder/store'; import * as THREE from "three"; import { useThree } from '@react-three/fiber'; import * as Types from "../../../types/world/worldTypes"; diff --git a/app/src/modules/visualization/zone/zoneCameraTarget.tsx b/app/src/modules/visualization/zone/zoneCameraTarget.tsx index 7927dc8..6ac0d3d 100644 --- a/app/src/modules/visualization/zone/zoneCameraTarget.tsx +++ b/app/src/modules/visualization/zone/zoneCameraTarget.tsx @@ -6,7 +6,7 @@ import { useEditPosition, usezonePosition, usezoneTarget, -} from "../../../store/store"; +} from "../../../store/builder/store"; export default function ZoneCentreTarget() { const { selectedZone } = useSelectedZoneStore(); diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index 04d5854..6857a5c 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -14,7 +14,7 @@ import { useZones, useLoadingProgress, useWidgetSubOption, -} from "../store/store"; +} from "../store/builder/store"; import { useNavigate } from "react-router-dom"; import { usePlayButtonStore } from "../store/usePlayButtonStore"; import MarketPlace from "../modules/market/MarketPlace"; @@ -31,6 +31,7 @@ import { useLogger } from "../components/ui/log/LoggerContext"; import RenderOverlay from "../components/templates/Overlay"; import LogList from "../components/ui/log/LogList"; import Footer from "../components/footer/Footer"; +import SelectFloorPlan from "../components/temporary/SelectFloorPlan"; const Project: React.FC = () => { let navigate = useNavigate(); @@ -98,6 +99,8 @@ const Project: React.FC = () => { {activeModule === "market" && } {activeModule !== "market" && } {isPlaying && activeModule === "simulation" && } + {/* remove this later */} + {activeModule === "builder" && !toggleThreeD && } )}
    { const { setTouch, setTemperature, setHumidity } = useDrieUIValue(); diff --git a/app/src/services/marketplace/fetchAssetDetails.ts b/app/src/services/marketplace/fetchAssetDetails.ts index 9c21b5d..cebefc2 100644 --- a/app/src/services/marketplace/fetchAssetDetails.ts +++ b/app/src/services/marketplace/fetchAssetDetails.ts @@ -1,5 +1,6 @@ let BackEnd_url = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; export const getAssetDetails = async (filename: string) => { + console.log('filename: ', filename); try { const response = await fetch(`${BackEnd_url}/api/v1/assetDetails`, { method: "POST", diff --git a/app/src/services/marketplace/fetchGltfUrl.ts b/app/src/services/marketplace/fetchGltfUrl.ts index 2f52887..aeef3f6 100644 --- a/app/src/services/marketplace/fetchGltfUrl.ts +++ b/app/src/services/marketplace/fetchGltfUrl.ts @@ -23,4 +23,4 @@ export const fetchGltfUrl = async (filename: string, AssetID: string) => { // throw new Error(error.message); } -} \ No newline at end of file +} diff --git a/app/src/store/builder/store.ts b/app/src/store/builder/store.ts new file mode 100644 index 0000000..06c7bfc --- /dev/null +++ b/app/src/store/builder/store.ts @@ -0,0 +1,454 @@ +import * as THREE from "three"; +import { create } from "zustand"; +import { io } from "socket.io-client"; +import * as CONSTANTS from '../../types/world/worldConstants'; + +export const useSocketStore = create((set: any, get: any) => ({ + socket: null, + initializeSocket: (email: string, organization: string) => { + const existingSocket = get().socket; + if (existingSocket) { + return; + } + + const socket = io( + `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`, + { + reconnection: true, + auth: { email, organization }, + } + ); + + const visualizationSocket = io( + `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Visualization`, + { + reconnection: true, + auth: { email, organization }, + } + ); + + set({ socket, visualizationSocket }); + }, + disconnectSocket: () => { + set((state: any) => { + state.socket?.disconnect(); + state.visualizationSocket?.disconnect(); + return { socket: null }; + }); + }, +})); + +export const useLoadingProgress = create<{ + loadingProgress: number; + setLoadingProgress: (x: number) => void; +}>((set) => ({ + loadingProgress: 1, + setLoadingProgress: (x: number) => set({ loadingProgress: x }), +})); + +export const useOrganization = create((set: any) => ({ + organization: "", + setOrganization: (x: any) => set(() => ({ organization: x })), +})); + +export const useToggleView = create((set: any) => ({ + toggleView: false, + setToggleView: (x: any) => set(() => ({ toggleView: x })), +})); + +export const useUpdateScene = create((set: any) => ({ + updateScene: false, + setUpdateScene: (x: any) => set(() => ({ updateScene: x })), +})); + +export const useWalls = create((set: any) => ({ + walls: [], + setWalls: (x: any) => set(() => ({ walls: x })), +})); + +export const useRoomsState = create((set: any) => ({ + roomsState: [], + setRoomsState: (x: any) => set(() => ({ roomsState: x })), +})); + +export const useZones = create((set: any) => ({ + zones: [], + setZones: (callback: any) => + set((state: any) => ({ + zones: typeof callback === "function" ? callback(state.zones) : callback, + })), +})); + +interface ZonePointsState { + zonePoints: THREE.Vector3[]; + setZonePoints: (points: THREE.Vector3[]) => void; +} + +export const useZonePoints = create((set) => ({ + zonePoints: [], + setZonePoints: (points) => set({ zonePoints: points }), +})); + +export const useSelectedItem = create((set: any) => ({ + selectedItem: { name: "", id: "", type: undefined, category: '', subCatergory: '' }, + setSelectedItem: (x: any) => set(() => ({ selectedItem: x })), +})); + +export const useNavMesh = create((set: any) => ({ + navMesh: null, + setNavMesh: (x: any) => set({ navMesh: x }), +})); + +export const useSelectedAssets = create((set: any) => ({ + selectedAssets: [], + setSelectedAssets: (x: any) => set(() => ({ selectedAssets: x })), +})); + +export const useLayers = create((set: any) => ({ + Layers: 1, + setLayers: (x: any) => set(() => ({ Layers: x })), +})); + +export const useCamPosition = create((set: any) => ({ + camPosition: { x: undefined, y: undefined, z: undefined }, + setCamPosition: (newCamPosition: any) => set({ camPosition: newCamPosition }), +})); + +export const useMenuVisible = create((set: any) => ({ + menuVisible: false, + setMenuVisible: (x: any) => set(() => ({ menuVisible: x })), +})); + +export const useDeleteTool = create((set: any) => ({ + deleteTool: false, + setDeleteTool: (x: any) => set(() => ({ deleteTool: x })), +})); + +export const useToolMode = create((set: any) => ({ + toolMode: null, + setToolMode: (x: any) => set(() => ({ toolMode: x })), +})); + +export const useNewLines = create((set: any) => ({ + newLines: [], + setNewLines: (x: any) => set(() => ({ newLines: x })), +})); + +export const useDeletedLines = create((set: any) => ({ + deletedLines: [], + setDeletedLines: (x: any) => set(() => ({ deletedLines: x })), +})); + +export const useMovePoint = create((set: any) => ({ + movePoint: false, + setMovePoint: (x: any) => set(() => ({ movePoint: x })), +})); + +export const useDeletePointOrLine = create((set: any) => ({ + deletePointOrLine: false, + setDeletePointOrLine: (x: any) => set(() => ({ deletePointOrLine: x })), +})); + +export const useFloorItems = create((set: any) => ({ + floorItems: null, + setFloorItems: (callback: any) => + set((state: any) => ({ + floorItems: + typeof callback === "function" ? callback(state.floorItems) : callback, + })), +})); + +export const useWallItems = create((set: any) => ({ + wallItems: [], + setWallItems: (callback: any) => + set((state: any) => ({ + wallItems: + typeof callback === "function" ? callback(state.wallItems) : callback, + })), +})); + +export const useSelectedWallItem = create((set: any) => ({ + selectedWallItem: null, + setSelectedWallItem: (x: any) => set(() => ({ selectedWallItem: x })), +})); + +export const useSelectedFloorItem = create((set: any) => ({ + selectedFloorItem: null, + setSelectedFloorItem: (x: any) => set(() => ({ selectedFloorItem: x })), +})); + +export const useDeletableFloorItem = create((set: any) => ({ + deletableFloorItem: null, + setDeletableFloorItem: (x: any) => set(() => ({ deletableFloorItem: x })), +})); + +export const useSetScale = create((set: any) => ({ + scale: null, + setScale: (x: any) => set(() => ({ scale: x })), +})); + +export const useRoofVisibility = create((set: any) => ({ + roofVisibility: false, + setRoofVisibility: (x: any) => set(() => ({ roofVisibility: x })), +})); + +export const useWallVisibility = create((set: any) => ({ + wallVisibility: false, + setWallVisibility: (x: any) => set(() => ({ wallVisibility: x })), +})); + +export const useShadows = create((set: any) => ({ + shadows: false, + setShadows: (x: any) => set(() => ({ shadows: x })), +})); + +export const useSunPosition = create((set: any) => ({ + sunPosition: { x: undefined, y: undefined, z: undefined }, + setSunPosition: (newSuntPosition: any) => + set({ sunPosition: newSuntPosition }), +})); + +export const useRemoveLayer = create((set: any) => ({ + removeLayer: false, + setRemoveLayer: (x: any) => set(() => ({ removeLayer: x })), +})); + +export const useRemovedLayer = create((set: any) => ({ + removedLayer: null, + setRemovedLayer: (x: any) => set(() => ({ removedLayer: x })), +})); + +export const useActiveLayer = create((set: any) => ({ + activeLayer: 1, + setActiveLayer: (x: any) => set({ activeLayer: x }), +})); + +interface RefTextUpdateState { + refTextupdate: number; + setRefTextUpdate: ( + callback: (currentValue: number) => number | number + ) => void; +} + +export const useRefTextUpdate = create((set) => ({ + refTextupdate: -1000, + setRefTextUpdate: (callback) => + set((state) => ({ + refTextupdate: + typeof callback === "function" + ? callback(state.refTextupdate) + : callback, + })), +})); + +export const useResetCamera = create((set: any) => ({ + resetCamera: false, + setResetCamera: (x: any) => set({ resetCamera: x }), +})); + +export const useAddAction = create((set: any) => ({ + addAction: null, + setAddAction: (x: any) => set({ addAction: x }), +})); + +export const useActiveTool = create((set: any) => ({ + activeTool: "cursor", + setActiveTool: (x: any) => set({ activeTool: x }), +})); + +export const useActiveSubTool = create((set: any) => ({ + activeSubTool: "cursor", + setActiveSubTool: (x: any) => set({ activeSubTool: x }), +})); + +export const use2DUndoRedo = create((set: any) => ({ + is2DUndoRedo: null, + set2DUndoRedo: (x: any) => set({ is2DUndoRedo: x }), +})); + +export const useElevation = create((set: any) => ({ + elevation: 45, + setElevation: (x: any) => set({ elevation: x }), +})); + +export const useAzimuth = create((set: any) => ({ + azimuth: -160, + setAzimuth: (x: any) => set({ azimuth: x }), +})); + +export const useRenderDistance = create((set: any) => ({ + renderDistance: 40, + setRenderDistance: (x: any) => set({ renderDistance: x }), +})); + +export const useCamMode = create((set: any) => ({ + camMode: "ThirdPerson", + setCamMode: (x: any) => set({ camMode: x }), +})); + +export const useUserName = create((set: any) => ({ + userName: "", + setUserName: (x: any) => set({ userName: x }), +})); + +export const useObjectPosition = create((set: any) => ({ + objectPosition: { x: undefined, y: undefined, z: undefined }, + setObjectPosition: (newObjectPosition: any) => + set({ objectPosition: newObjectPosition }), +})); + +export const useObjectRotation = create((set: any) => ({ + objectRotation: { x: undefined, y: undefined, z: undefined }, + setObjectRotation: (newObjectRotation: any) => + set({ objectRotation: newObjectRotation }), +})); + +export const useDrieTemp = create((set: any) => ({ + drieTemp: undefined, + setDrieTemp: (x: any) => set({ drieTemp: x }), +})); + +export const useActiveUsers = create((set: any) => ({ + activeUsers: [], + setActiveUsers: (callback: (prev: any[]) => any[] | any[]) => + set((state: { activeUsers: any[] }) => ({ + activeUsers: + typeof callback === "function" ? callback(state.activeUsers) : callback, + })), +})); + +export const useDrieUIValue = create((set: any) => ({ + drieUIValue: { touch: null, temperature: null, humidity: null }, + + setDrieUIValue: (x: any) => + set((state: any) => ({ drieUIValue: { ...state.drieUIValue, ...x } })), + + setTouch: (value: any) => + set((state: any) => ({ + drieUIValue: { ...state.drieUIValue, touch: value }, + })), + setTemperature: (value: any) => + set((state: any) => ({ + drieUIValue: { ...state.drieUIValue, temperature: value }, + })), + setHumidity: (value: any) => + set((state: any) => ({ + drieUIValue: { ...state.drieUIValue, humidity: value }, + })), +})); + +export const useStartSimulation = create((set: any) => ({ + startSimulation: false, + setStartSimulation: (x: any) => set({ startSimulation: x }), +})); + +export const useEyeDropMode = create((set: any) => ({ + eyeDropMode: false, + setEyeDropMode: (x: any) => set({ eyeDropMode: x }), +})); + +export const useEditingPoint = create((set: any) => ({ + editingPoint: false, + setEditingPoint: (x: any) => set({ editingPoint: x }), +})); + +export const usezoneTarget = create((set: any) => ({ + zoneTarget: [], + setZoneTarget: (x: any) => set({ zoneTarget: x }), +})); + +export const usezonePosition = create((set: any) => ({ + zonePosition: [], + setZonePosition: (x: any) => set({ zonePosition: x }), +})); + +interface EditPositionState { + Edit: boolean; + setEdit: (value: boolean) => void; +} + +export const useEditPosition = create((set) => ({ + Edit: false, + setEdit: (value) => set({ Edit: value }), +})); + +export const useAsset3dWidget = create((set: any) => ({ + widgetSelect: "", + setWidgetSelect: (x: any) => set({ widgetSelect: x }), +})); + +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 }), +})); + +export const useTileDistance = create((set: any) => ({ + gridValue: { size: CONSTANTS.gridConfig.size, divisions: CONSTANTS.gridConfig.divisions }, + planeValue: { height: CONSTANTS.planeConfig.height, width: CONSTANTS.planeConfig.width }, + + setGridValue: (value: any) => + set((state: any) => ({ + gridValue: { ...state.gridValue, ...value }, + })), + + setPlaneValue: (value: any) => + set((state: any) => ({ + planeValue: { ...state.planeValue, ...value }, + })), +})); + +export const usePlayAgv = create((set, get) => ({ + PlayAgv: [], + setPlayAgv: (updateFn: (prev: any[]) => any[]) => + set({ PlayAgv: updateFn(get().PlayAgv) }), +})); + +// Define the Asset type +type Asset = { + id: string; + name: string; + position?: [number, number, number]; // Optional: 3D position + rotation?: { x: number; y: number; z: number }; // Optional: Euler rotation +}; + +// Zustand store type +type ZoneAssetState = { + zoneAssetId: Asset | null; + setZoneAssetId: (asset: Asset | null) => void; +}; + +// Zustand store +export const useZoneAssetId = create((set) => ({ + zoneAssetId: null, + setZoneAssetId: (asset) => set({ zoneAssetId: asset }), +})); + +// version visible hidden +interface VersionHistoryState { + viewVersionHistory: boolean; + setVersionHistory: (value: boolean) => void; +} + +const useVersionHistoryStore = create((set) => ({ + viewVersionHistory: false, + setVersionHistory: (value) => set({ viewVersionHistory: value }), +})); + +export default useVersionHistoryStore; + +interface ShortcutStore { + showShortcuts: boolean; + setShowShortcuts: (value: boolean) => void; + toggleShortcuts: () => void; +} + +export const useShortcutStore = create((set) => ({ + showShortcuts: false, + setShowShortcuts: (value) => set({ showShortcuts: value }), + toggleShortcuts: () => + set((state) => ({ showShortcuts: !state.showShortcuts })), +})); \ No newline at end of file diff --git a/app/src/store/builder/uselayoutStore.ts b/app/src/store/builder/uselayoutStore.ts new file mode 100644 index 0000000..acaeb50 --- /dev/null +++ b/app/src/store/builder/uselayoutStore.ts @@ -0,0 +1,37 @@ +import { create } from 'zustand'; + +type Layout = null | 'layout1' | 'layout2'; + +type LayoutState = { + currentLayout: Layout; + setLayout: (layout: Layout) => void; + resetLayout: () => void; +}; + +const LAYOUT_STORAGE_KEY = 'currentLayout'; + +const useLayoutStore = create((set) => ({ + currentLayout: (() => { + if (typeof window !== 'undefined') { + const storedLayout = localStorage.getItem(LAYOUT_STORAGE_KEY); + return storedLayout ? (JSON.parse(storedLayout) as Layout) : null; + } + return null; + })(), + + setLayout: (layout) => { + if (typeof window !== 'undefined') { + localStorage.setItem(LAYOUT_STORAGE_KEY, JSON.stringify(layout)); + } + set({ currentLayout: layout }); + }, + + resetLayout: () => { + if (typeof window !== 'undefined') { + localStorage.removeItem(LAYOUT_STORAGE_KEY); + } + set({ currentLayout: null }); + }, +})); + +export default useLayoutStore; \ No newline at end of file diff --git a/app/src/store/store.ts b/app/src/store/store.ts deleted file mode 100644 index 61d84d5..0000000 --- a/app/src/store/store.ts +++ /dev/null @@ -1,437 +0,0 @@ -import * as THREE from "three"; -import * as Types from "../types/world/worldTypes"; -import { create } from "zustand"; -import { io } from "socket.io-client"; - -export const useSocketStore = create((set: any, get: any) => ({ - socket: null, - initializeSocket: (email: string, organization: string) => { - const existingSocket = get().socket; - if (existingSocket) { - return; - } - - const socket = io( - `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`, - { - reconnection: true, - auth: { email, organization }, - } - ); - - const visualizationSocket = io( - `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Visualization`, - { - reconnection: true, - auth: { email, organization }, - } - ); - - set({ socket, visualizationSocket }); - }, - disconnectSocket: () => { - set((state: any) => { - state.socket?.disconnect(); - state.visualizationSocket?.disconnect(); - return { socket: null }; - }); - }, -})); - -export const useLoadingProgress = create<{ - loadingProgress: number; - setLoadingProgress: (x: number) => void; -}>((set) => ({ - loadingProgress: 1, - setLoadingProgress: (x: number) => set({ loadingProgress: x }), -})); - -export const useOrganization = create((set: any) => ({ - organization: "", - setOrganization: (x: any) => set(() => ({ organization: x })), -})); - -export const useToggleView = create((set: any) => ({ - toggleView: false, - setToggleView: (x: any) => set(() => ({ toggleView: x })), -})); - -export const useUpdateScene = create((set: any) => ({ - updateScene: false, - setUpdateScene: (x: any) => set(() => ({ updateScene: x })), -})); - -export const useWalls = create((set: any) => ({ - walls: [], - setWalls: (x: any) => set(() => ({ walls: x })), -})); - -export const useRoomsState = create((set: any) => ({ - roomsState: [], - setRoomsState: (x: any) => set(() => ({ roomsState: x })), -})); - -export const useZones = create((set: any) => ({ - zones: [], - setZones: (callback: any) => - set((state: any) => ({ - zones: typeof callback === "function" ? callback(state.zones) : callback, - })), -})); - -interface ZonePointsState { - zonePoints: THREE.Vector3[]; - setZonePoints: (points: THREE.Vector3[]) => void; -} - -export const useZonePoints = create((set) => ({ - zonePoints: [], - setZonePoints: (points) => set({ zonePoints: points }), -})); - -export const useSelectedItem = create((set: any) => ({ - selectedItem: { name: "", id: "", type: undefined }, - setSelectedItem: (x: any) => set(() => ({ selectedItem: x })), -})); - -export const useNavMesh = create((set: any) => ({ - navMesh: null, - setNavMesh: (x: any) => set({ navMesh: x }), -})); - -export const useSelectedAssets = create((set: any) => ({ - selectedAssets: [], - setSelectedAssets: (x: any) => set(() => ({ selectedAssets: x })), -})); - -export const useLayers = create((set: any) => ({ - Layers: 1, - setLayers: (x: any) => set(() => ({ Layers: x })), -})); - -export const useCamPosition = create((set: any) => ({ - camPosition: { x: undefined, y: undefined, z: undefined }, - setCamPosition: (newCamPosition: any) => set({ camPosition: newCamPosition }), -})); - -export const useMenuVisible = create((set: any) => ({ - menuVisible: false, - setMenuVisible: (x: any) => set(() => ({ menuVisible: x })), -})); - -export const useDeleteTool = create((set: any) => ({ - deleteTool: false, - setDeleteTool: (x: any) => set(() => ({ deleteTool: x })), -})); - -export const useToolMode = create((set: any) => ({ - toolMode: null, - setToolMode: (x: any) => set(() => ({ toolMode: x })), -})); - -export const useNewLines = create((set: any) => ({ - newLines: [], - setNewLines: (x: any) => set(() => ({ newLines: x })), -})); - -export const useDeletedLines = create((set: any) => ({ - deletedLines: [], - setDeletedLines: (x: any) => set(() => ({ deletedLines: x })), -})); - -export const useMovePoint = create((set: any) => ({ - movePoint: false, - setMovePoint: (x: any) => set(() => ({ movePoint: x })), -})); - -export const useTransformMode = create((set: any) => ({ - transformMode: null, - setTransformMode: (x: any) => set(() => ({ transformMode: x })), -})); - -export const useDeletePointOrLine = create((set: any) => ({ - deletePointOrLine: false, - setDeletePointOrLine: (x: any) => set(() => ({ deletePointOrLine: x })), -})); - -export const useFloorItems = create((set: any) => ({ - floorItems: null, - setFloorItems: (callback: any) => - set((state: any) => ({ - floorItems: - typeof callback === "function" ? callback(state.floorItems) : callback, - })), -})); - -export const useWallItems = create((set: any) => ({ - wallItems: [], - setWallItems: (callback: any) => - set((state: any) => ({ - wallItems: - typeof callback === "function" ? callback(state.wallItems) : callback, - })), -})); - -export const useSelectedWallItem = create((set: any) => ({ - selectedWallItem: null, - setSelectedWallItem: (x: any) => set(() => ({ selectedWallItem: x })), -})); - -export const useSelectedFloorItem = create((set: any) => ({ - selectedFloorItem: null, - setSelectedFloorItem: (x: any) => set(() => ({ selectedFloorItem: x })), -})); - -export const useDeletableFloorItem = create((set: any) => ({ - deletableFloorItem: null, - setDeletableFloorItem: (x: any) => set(() => ({ deletableFloorItem: x })), -})); - -export const useSetScale = create((set: any) => ({ - scale: null, - setScale: (x: any) => set(() => ({ scale: x })), -})); - -export const useRoofVisibility = create((set: any) => ({ - roofVisibility: false, - setRoofVisibility: (x: any) => set(() => ({ roofVisibility: x })), -})); - -export const useWallVisibility = create((set: any) => ({ - wallVisibility: false, - setWallVisibility: (x: any) => set(() => ({ wallVisibility: x })), -})); - -export const useShadows = create((set: any) => ({ - shadows: false, - setShadows: (x: any) => set(() => ({ shadows: x })), -})); - -export const useSunPosition = create((set: any) => ({ - sunPosition: { x: undefined, y: undefined, z: undefined }, - setSunPosition: (newSuntPosition: any) => - set({ sunPosition: newSuntPosition }), -})); - -export const useRemoveLayer = create((set: any) => ({ - removeLayer: false, - setRemoveLayer: (x: any) => set(() => ({ removeLayer: x })), -})); - -export const useRemovedLayer = create((set: any) => ({ - removedLayer: null, - setRemovedLayer: (x: any) => set(() => ({ removedLayer: x })), -})); - -export const useActiveLayer = create((set: any) => ({ - activeLayer: 1, - setActiveLayer: (x: any) => set({ activeLayer: x }), -})); - -interface RefTextUpdateState { - refTextupdate: number; - setRefTextUpdate: ( - callback: (currentValue: number) => number | number - ) => void; -} - -export const useRefTextUpdate = create((set) => ({ - refTextupdate: -1000, - setRefTextUpdate: (callback) => - set((state) => ({ - refTextupdate: - typeof callback === "function" - ? callback(state.refTextupdate) - : callback, - })), -})); - -export const useResetCamera = create((set: any) => ({ - resetCamera: false, - setResetCamera: (x: any) => set({ resetCamera: x }), -})); - -export const useAddAction = create((set: any) => ({ - addAction: null, - setAddAction: (x: any) => set({ addAction: x }), -})); - -export const useActiveTool = create((set: any) => ({ - activeTool: "cursor", - setActiveTool: (x: any) => set({ activeTool: x }), -})); - -export const useActiveSubTool = create((set: any) => ({ - activeSubTool: "cursor", - setActiveSubTool: (x: any) => set({ activeSubTool: x }), -})); - -export const use2DUndoRedo = create((set: any) => ({ - is2DUndoRedo: null, - set2DUndoRedo: (x: any) => set({ is2DUndoRedo: x }), -})); - -export const useElevation = create((set: any) => ({ - elevation: 45, - setElevation: (x: any) => set({ elevation: x }), -})); - -export const useAzimuth = create((set: any) => ({ - azimuth: -160, - setAzimuth: (x: any) => set({ azimuth: x }), -})); - -export const useRenderDistance = create((set: any) => ({ - renderDistance: 40, - setRenderDistance: (x: any) => set({ renderDistance: x }), -})); - -export const useCamMode = create((set: any) => ({ - camMode: "ThirdPerson", - setCamMode: (x: any) => set({ camMode: x }), -})); - -export const useUserName = create((set: any) => ({ - userName: "", - setUserName: (x: any) => set({ userName: x }), -})); - -export const useObjectPosition = create((set: any) => ({ - objectPosition: { x: undefined, y: undefined, z: undefined }, - setObjectPosition: (newObjectPosition: any) => - set({ objectPosition: newObjectPosition }), -})); - -export const useObjectScale = create((set: any) => ({ - objectScale: { x: undefined, y: undefined, z: undefined }, - setObjectScale: (newObjectScale: any) => set({ objectScale: newObjectScale }), -})); - -export const useObjectRotation = create((set: any) => ({ - objectRotation: { x: undefined, y: undefined, z: undefined }, - setObjectRotation: (newObjectRotation: any) => - set({ objectRotation: newObjectRotation }), -})); - -export const useDrieTemp = create((set: any) => ({ - drieTemp: undefined, - setDrieTemp: (x: any) => set({ drieTemp: x }), -})); - -export const useActiveUsers = create((set: any) => ({ - activeUsers: [], - setActiveUsers: (callback: (prev: any[]) => any[] | any[]) => - set((state: { activeUsers: any[] }) => ({ - activeUsers: - typeof callback === "function" ? callback(state.activeUsers) : callback, - })), -})); - -export const useDrieUIValue = create((set: any) => ({ - drieUIValue: { touch: null, temperature: null, humidity: null }, - - setDrieUIValue: (x: any) => - set((state: any) => ({ drieUIValue: { ...state.drieUIValue, ...x } })), - - setTouch: (value: any) => - set((state: any) => ({ - drieUIValue: { ...state.drieUIValue, touch: value }, - })), - setTemperature: (value: any) => - set((state: any) => ({ - drieUIValue: { ...state.drieUIValue, temperature: value }, - })), - setHumidity: (value: any) => - set((state: any) => ({ - drieUIValue: { ...state.drieUIValue, humidity: value }, - })), -})); - -export const useStartSimulation = create((set: any) => ({ - startSimulation: false, - setStartSimulation: (x: any) => set({ startSimulation: x }), -})); - -export const useEyeDropMode = create((set: any) => ({ - eyeDropMode: false, - setEyeDropMode: (x: any) => set({ eyeDropMode: x }), -})); - -export const useEditingPoint = create((set: any) => ({ - editingPoint: false, - setEditingPoint: (x: any) => set({ editingPoint: x }), -})); - -export const usezoneTarget = create((set: any) => ({ - zoneTarget: [], - setZoneTarget: (x: any) => set({ zoneTarget: x }), -})); - -export const usezonePosition = create((set: any) => ({ - zonePosition: [], - setZonePosition: (x: any) => set({ zonePosition: x }), -})); - -interface EditPositionState { - Edit: boolean; - setEdit: (value: boolean) => void; -} - -export const useEditPosition = create((set) => ({ - Edit: false, - setEdit: (value) => set({ Edit: value }), -})); - -export const useAsset3dWidget = create((set: any) => ({ - widgetSelect: "", - setWidgetSelect: (x: any) => set({ widgetSelect: x }), -})); - -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 }), -})); - -export const useTileDistance = create((set: any) => ({ - gridValue: { size: 300, divisions: 75 }, - planeValue: { height: 300, width: 300 }, - - setGridValue: (value: any) => - set((state: any) => ({ - gridValue: { ...state.gridValue, ...value }, - })), - - setPlaneValue: (value: any) => - set((state: any) => ({ - planeValue: { ...state.planeValue, ...value }, - })), -})); - -export const usePlayAgv = create((set, get) => ({ - PlayAgv: [], - setPlayAgv: (updateFn: (prev: any[]) => any[]) => - set({ PlayAgv: updateFn(get().PlayAgv) }), -})); -// Define the Asset type -type Asset = { - id: string; - name: string; - position?: [number, number, number]; // Optional: 3D position - rotation?: { x: number; y: number; z: number }; // Optional: Euler rotation -}; - -// Zustand store type -type ZoneAssetState = { - zoneAssetId: Asset | null; - setZoneAssetId: (asset: Asset | null) => void; -}; - -// Zustand store -export const useZoneAssetId = create((set) => ({ - zoneAssetId: null, - setZoneAssetId: (asset) => set({ zoneAssetId: asset }), -})); diff --git a/app/src/store/visualization/useDroppedObjectsStore.ts b/app/src/store/visualization/useDroppedObjectsStore.ts index bbe4cde..0655293 100644 --- a/app/src/store/visualization/useDroppedObjectsStore.ts +++ b/app/src/store/visualization/useDroppedObjectsStore.ts @@ -1,5 +1,5 @@ import { create } from "zustand"; -import { useSocketStore } from "../store"; +import { useSocketStore } from "../builder/store"; import useChartStore from "./useChartStore"; type DroppedObject = { diff --git a/app/src/styles/components/footer/footer.scss b/app/src/styles/components/footer/footer.scss index c7f999b..728e222 100644 --- a/app/src/styles/components/footer/footer.scss +++ b/app/src/styles/components/footer/footer.scss @@ -1,156 +1,318 @@ @use "../../abstracts/variables" as *; @use "../../abstracts/mixins" as *; -.footer-wrapper { + +.footer-container { + width: 100%; position: absolute; bottom: 0; left: 0; - width: 100%; - z-index: 1; - display: flex; - justify-content: space-between; + z-index: 3; padding: 2px 12px; + display: flex; + flex-direction: column; + gap: 6px; - .selection-wrapper { + .footer-wrapper { display: flex; - gap: 6px; + justify-content: space-between; - .selector-wrapper { + .selection-wrapper { display: flex; gap: 6px; - align-items: center; - background: var(--background-color); - padding: 3px 6px; - border-radius: #{$border-radius-large}; - color: var(--text-color); - backdrop-filter: blur(14px); - .selector { + .selector-wrapper { + display: flex; + gap: 6px; + align-items: center; + background: var(--background-color); + padding: 3px 6px; + border-radius: #{$border-radius-large}; color: var(--text-color); - font-size: var(--font-size-small) + backdrop-filter: blur(14px); + + .selector { + color: var(--text-color); + font-size: var(--font-size-small) + } + + .icon { + height: 17px; + } + } + } + + .logs-wrapper { + display: flex; + gap: 6px; + position: relative; + + // dummy + .bg-dummy { + background: var(--background-color-solid); + position: absolute; + z-index: -1; } - .icon { - height: 17px; + .bg-dummy.left-top { + top: 1px; + left: 4px; + width: 60%; + height: 16px; + border-radius: #{$border-radius-extra-large}; + } + + .bg-dummy.right-bottom { + right: 68px; + bottom: 0; + width: 20%; + height: 100%; + border-radius: #{$border-radius-circle}; + } + + .log-container { + background: var(--background-color); + backdrop-filter: blur(20px); + border-radius: #{$border-radius-large}; + @include flex-center; + overflow: hidden; + } + + .logs-detail, + .version { + @include flex-center; + border-radius: #{$border-radius-extra-large}; + padding: 3px 6px; + height: 100%; + color: var(--text-color); + gap: 6px; + } + + .logs-detail { + padding: 2px 12px; + cursor: pointer; + outline: 0 solid var(--border-color); + outline-offset: -1px; + + .log-icon { + @include flex-center; + } + + .log-message { + max-width: 40vw; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } + + .version { + background: var(--background-color); + font-size: var(--font-size-tiny); + + .icon { + @include flex-center; + } + } + + .log { + background: var(--log-default-background-color); + outline-color: var(--default-text-color); + + .log-message { + color: var(--default-text-color); + } + } + + .info { + background: var(--log-info-background-color); + outline-color: var(--log-info-text-color); + + .log-message { + color: var(--log-info-text-color); + } + } + + .error { + background: var(--log-error-background-color); + outline-color: var(--log-error-text-color); + + .log-message { + color: var(--log-error-text-color); + } + } + + .warning { + background: var(--log-warn-background-color); + outline-color: var(--log-warn-text-color); + + .log-message { + color: var(--log-warn-text-color); + } + } + + .success { + background: var(--log-success-background-color); + + .log-message { + color: var(--log-success-text-color); + } } } } - .logs-wrapper { - display: flex; - gap: 6px; - position: relative; + .shortcut-helper-container { + min-height: 320px; + height: 320px; + border-radius: 18px; - // dummy - .bg-dummy { - background: var(--background-color-solid); - position: absolute; - z-index: -1; + + background: var(--background-color); + backdrop-filter: blur(20px); + + .header { + display: flex; + justify-content: center; + flex-wrap: wrap; + gap: 10px; + padding: 12px 0; + + .header-wrapper { + display: flex; + align-items: center; + + button, + .type { + font-size: var(--font-size-small); + } + + .type { + position: relative; + padding-left: 10px; + + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 1px; + height: 100%; + background: var(--text-color); + } + } + } + + button { + padding: 4px 12px; + cursor: pointer; + border-radius: 4px; + background-color: transparent; + border: 1px solid transparent; + transition: background 0.2s; + + &.active { + background: var(--background-color-button); + color: var(--icon-default-color-active); + border-radius: 18px; + } + } } - .bg-dummy.left-top { - top: 1px; - left: 4px; - width: 60%; - height: 16px; - border-radius: #{$border-radius-extra-large}; - } - - .bg-dummy.right-bottom { - right: 68px; - bottom: 0; - width: 20%; - height: 100%; - border-radius: #{$border-radius-circle}; - } - - .log-container { + .shortcut-wrapper { + padding: 16px 0; + margin: 0 12px; + height: calc(100% - 60px); + display: grid; + grid-template-columns: repeat(2, 350px); + justify-content: center; + align-content: start; // Align content to top + gap: 16px; + overflow-y: auto; // Allow scrolling if content is too tall + border-radius: 18px; background: var(--background-color); - backdrop-filter: blur(20px); - border-radius: #{$border-radius-large}; - @include flex-center; - overflow: hidden; - } - .logs-detail, - .version { - @include flex-center; - border-radius: #{$border-radius-extra-large}; - padding: 3px 6px; - height: 100%; - color: var(--text-color); - gap: 6px; - } + .shortcut-item { + min-width: 330px; + display: flex; + justify-content: space-between; + align-items: flex-start; + padding: 0px 16px; + border-radius: 6px; + box-sizing: border-box; + gap: 12px; - .logs-detail { - padding: 2px 12px; - cursor: pointer; - outline: 0 solid var(--border-color); - outline-offset: -1px; + .shortcut-intro { + display: flex; + // align-items: center; + gap: 6px; - .log-icon { - @include flex-center; - } + .value-wrapper { + .name { + font-size: var(--font-size-small); + } - .log-message { - max-width: 40vw; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + .description { + font-size: var(--font-size-tiny); + + } + } + } + + .keys { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; + + + .key { + background: linear-gradient(135.11deg, #656DC2 3.48%, #9526E5 91.33%); + padding: 4px 10px; + border-radius: 4px; + font-family: monospace; + font-size: var(--font-size-tiny); + color: var(--icon-default-color-active); + } + + .key.add { + background: none; + padding: 0; + } + } + + .description { + font-size: 14px; + color: var(--input-text-color); + line-height: 1.4; + } } } - .version { - background: var(--background-color); - font-size: var(--font-size-tiny); - - .icon { - @include flex-center; - } + .shortcut-wrapper.single-item { + display: flex; + justify-content: center; + align-items: flex-start; + /* or center if vertical centering is desired */ } - .log { - background: var(--log-default-background-color); - outline-color: var(--default-text-color); - .log-message { - color: var(--default-text-color); - } - } - .info { - background: var(--log-info-background-color); - outline-color: var(--log-info-text-color); + } - .log-message { - color: var(--log-info-text-color); - } - } - .error { - background: var(--log-error-background-color); - outline-color: var(--log-error-text-color); - .log-message { - color: var(--log-error-text-color); - } - } +} - .warning { - background: var(--log-warn-background-color); - outline-color: var(--log-warn-text-color); +.shortcut-helper-overlay { + max-height: 0; + overflow: hidden; + // opacity: 0; + transform: translateY(20px); + transition: all 0.3s ease-in-out; - .log-message { - color: var(--log-warn-text-color); - } - } - - .success { - background: var(--log-success-background-color); - - .log-message { - color: var(--log-success-text-color); - } - } + &.visible { + max-height: 1000px; // adjust as needed + opacity: 1; + transform: translateY(0); } } \ No newline at end of file diff --git a/app/src/styles/components/lists.scss b/app/src/styles/components/lists.scss index 27ccda8..825999d 100644 --- a/app/src/styles/components/lists.scss +++ b/app/src/styles/components/lists.scss @@ -42,15 +42,16 @@ text-align: center; padding: 4px 8px; border-radius: #{$border-radius-large}; + .value { + width: 100%; + max-width: 180px; + overflow: hidden; + text-overflow: ellipsis; + text-align: start; + } .zone-header { @include flex-center; - .value { - width: 100%; - text-align: start; - max-width: 180px; - } } - .options-container { @include flex-center; gap: 6px; @@ -61,12 +62,21 @@ } } } + .active { background: var(--highlight-accent-color); + .input-value { color: var(--highlight-text-color); } } + + &:hover { + cursor: pointer; + .list-item { + background: var(--highlight-accent-color); + } + } } .asset-list { diff --git a/app/src/styles/components/marketPlace/marketPlace.scss b/app/src/styles/components/marketPlace/marketPlace.scss index 6e39da6..30d2b48 100644 --- a/app/src/styles/components/marketPlace/marketPlace.scss +++ b/app/src/styles/components/marketPlace/marketPlace.scss @@ -179,7 +179,7 @@ position: relative; display: flex; flex-direction: column; - justify-content: center; + justify-content: space-between; gap: 6px; .icon { @@ -209,6 +209,7 @@ } .assets-container { + height: auto; display: flex; justify-content: space-between; padding: 0; @@ -266,9 +267,11 @@ padding: 8px 0; @include flex-center; color: var(--text-button-color); + transition: all 0.1s linear; &:hover { cursor: pointer; + transform: translateY(-2px); } } } diff --git a/app/src/styles/components/menu/menu.scss b/app/src/styles/components/menu/menu.scss index d25173c..4a27f4f 100644 --- a/app/src/styles/components/menu/menu.scss +++ b/app/src/styles/components/menu/menu.scss @@ -102,6 +102,7 @@ padding: 4px; .menu-item-container { position: relative; + width: 100%; .menu-item { padding: 4px 8px 4px 12px; border-radius: #{$border-radius-medium}; diff --git a/app/src/styles/components/tools.scss b/app/src/styles/components/tools.scss index 3fe49e5..05e991a 100644 --- a/app/src/styles/components/tools.scss +++ b/app/src/styles/components/tools.scss @@ -47,11 +47,10 @@ position: relative; &:hover { - background: color-mix( - in srgb, - var(--highlight-accent-color) 60%, - transparent - ); + background: color-mix(in srgb, + var(--highlight-accent-color) 60%, + transparent); + .tooltip { opacity: 1; transform: translateY(-2px); @@ -79,11 +78,9 @@ position: relative; &:hover { - background: color-mix( - in srgb, - var(--highlight-accent-color) 60%, - transparent - ); + background: color-mix(in srgb, + var(--highlight-accent-color) 60%, + transparent); } .drop-down-container { @@ -180,6 +177,16 @@ } } + +.tools-container { + transition: transform 0.4s ease-in-out 0.01s; + + &.visible { + transform: translate(-50%, -310px); + } +} + + .exitPlay { width: 30px; height: 30px; @@ -195,14 +202,17 @@ z-index: 100; isolation: isolate; font-weight: 700; + &:hover { font-weight: 500; background: var(--accent-color); color: var(--highlight-accent-color); + &::after { animation: pulse 1s ease-out infinite; } } + &::after { content: ""; position: absolute; @@ -219,9 +229,11 @@ opacity: 0; scale: 0.5; } + 50% { opacity: 1; } + 100% { opacity: 0; scale: 2; @@ -238,4 +250,4 @@ width: fit-content; opacity: 1; } -} +} \ No newline at end of file diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index 6fcf8ac..b34be14 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -467,6 +467,168 @@ position: relative; width: 304px; + .version-history-container { + max-height: calc(62vh - 12px); + display: flex; + flex-direction: column; + padding: 0 8px; + gap: 10px; + + .version-history-header { + display: flex; + justify-content: space-between; + align-items: center; + + .version-history-icons { + display: flex; + align-items: center; + gap: 6px; + + .add-icon { + transform: scale(1.1); + + + + } + + .kebab-icon { + display: flex; + transform: rotate(90deg) scale(0.8); + } + + .close-icon { + transform: scale(1.5); + } + } + } + + .version-history-shortcut-info { + display: flex; + gap: 6px; + border: 1px solid var(--border-color); + + background: var(--background-color); + padding: 10px 8px; + border-radius: 13px; + + .info-icon { + width: 12px; + height: 12px; + display: flex; + justify-content: center; + align-items: center; + border-radius: 50%; + border: 1px solid var(--border-color); + padding: 4px; + font-size: 10px; + } + + .shortcut-text { + color: var(--text-disabled); + } + } + + .version-history-location { + display: flex; + align-items: center; + gap: 12px; + + .location-details { + display: flex; + flex-direction: column; + gap: 4px; + + .saved-history-count { + font-size: var(--font-size-tiny) + } + } + } + + .saved-versions-list { + padding-top: 16px; + display: flex; + flex-direction: column; + gap: 20px; + + .saved-version { + display: flex; + align-items: center; + gap: 12px; + + .version-name { + + background: var(--background-color); + border: 1px solid var(--border-color); + color: var(--text-color); + border-radius: 13px; + padding: 4px 8px; + position: relative; + font-size: var(--font-size-small); + } + + &:not(:first-child) .version-name::after { + content: ""; + position: absolute; + top: -35px; + left: 50%; + transform: translateX(-50%); + width: 1px; + height: 32px; + background-color: var(--text-disabled); + } + + .version-details { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + gap: 6px; + + .details { + display: flex; + flex-direction: column; + gap: 6px; + + } + + .saved-by { + display: flex; + align-items: center; + gap: 6px; + + .user-profile { + + background: var(--background-color-accent); + color: var(--text-button-color); + width: 20px; + height: 20px; + border-radius: 50%; + + display: flex; + justify-content: center; + align-items: center; + text-transform: uppercase; + } + + .user-name { + text-transform: capitalize; + + } + } + + .timestamp { + .input-value { + color: var(--text-color); + text-align: start; + } + } + } + } + } + + + } + .no-event-selected { color: #666; padding: 16px; @@ -524,6 +686,7 @@ max-height: 60vh; .sidebar-right-content-container { + .dataSideBar { .inputs-wrapper { display: flex; @@ -1006,6 +1169,7 @@ margin: 6px 0; padding-left: 16px; position: relative; + &::after { content: "↶"; rotate: -90deg; @@ -1019,6 +1183,7 @@ top: 0; left: 4px; } + &:last-child { &::after { display: none; @@ -1354,6 +1519,7 @@ &:hover { outline: 1px solid var(--border-color-accent); + cursor: pointer; img { transition: all 0.2s; @@ -1488,11 +1654,9 @@ width: 100%; height: 100%; font-size: var(--font-size-regular); - background: linear-gradient( - 0deg, - rgba(37, 24, 51, 0) 0%, - rgba(52, 41, 61, 0.5) 100% - ); + background: linear-gradient(0deg, + rgba(37, 24, 51, 0) 0%, + rgba(52, 41, 61, 0.5) 100%); pointer-events: none; backdrop-filter: blur(8px); opacity: 0; @@ -1531,6 +1695,7 @@ .sidebar-right-wrapper.open { height: fit-content; animation: openSidebar 0.2s linear; + .sidebar-right-container, .sidebar-left-container { opacity: 0; @@ -1542,6 +1707,7 @@ from { opacity: 0; } + to { opacity: 1; } @@ -1551,6 +1717,7 @@ from { height: 60%; } + to { height: 52px; } @@ -1560,7 +1727,8 @@ from { height: 52px; } + to { height: 60%; } -} +} \ No newline at end of file diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index 9a072e7..e454a34 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -21,9 +21,9 @@ } .floating { - width: calc(var(--realTimeViz-container-width) * 0.2px); + // width: calc(var(--realTimeViz-container-width) * 0.2px); - transform: scale(min(1, calc(var(--realTimeViz-container-width) / 1000))); + // transform: scale(min(1, calc(var(--realTimeViz-container-width) / 1000))); min-width: 230px; max-width: 300px; diff --git a/app/src/styles/scene/scene.scss b/app/src/styles/scene/scene.scss index ff7fec3..2265359 100644 --- a/app/src/styles/scene/scene.scss +++ b/app/src/styles/scene/scene.scss @@ -19,7 +19,7 @@ border-radius: #{$border-radius-medium}; box-shadow: var(--box-shadow-light); } - .area{ + .area { background: #008cff; } } @@ -27,3 +27,34 @@ .pointer-none { pointer-events: none; } + +// temp +.select-floorplane-wrapper { + position: absolute; + @include flex-center; + gap: 12px; + top: 0; + left: 50%; + padding: 8px; + padding-left: 14px; + background: var(--background-color); + backdrop-filter: blur(12px); + border-radius: #{$border-radius-large}; + outline: 1px solid var(--border-color); + transform: translate(-50%, 12px); + z-index: 100; + .presets-container { + @include flex-center; + gap: 4px; + .preset { + background: var(--background-color); + padding: 2px 8px; + border-radius: #{$border-radius-large}; + outline: 1px solid var(--border-color); + } + .active{ + background: var(--background-color-accent); + color: var(--text-button-color); + } + } +} diff --git a/app/src/types/world/worldConstants.ts b/app/src/types/world/worldConstants.ts index aaa8466..5193ee1 100644 --- a/app/src/types/world/worldConstants.ts +++ b/app/src/types/world/worldConstants.ts @@ -227,22 +227,10 @@ export const twoDimension: TwoDimension = { rightMouse: 0, // Mouse button for no action }; -export const threeDimension: ThreeDimension = { - defaultPosition: [0, 40, 30], // Default position of the camera - defaultTarget: [0, 0, 0], // Default target of the camera - defaultRotation: [0, 0, 0], // Default rotation of the camera - defaultAzimuth: 0, // Default azimuth of the camera - boundaryBottom: [-150, 0, -150], // Bottom boundary of the camera movement - boundaryTop: [150, 100, 150], // Top boundary of the camera movement - minDistance: 1, // Minimum distance from the target - leftMouse: 2, // Mouse button for panning - rightMouse: 1, // Mouse button for rotation -}; - export const camPositionUpdateInterval: number = 200; // Interval for updating the camera position export const gridConfig: GridConfig = { - size: 300, // Size of the grid + size: 150, // Size of the grid divisions: 75, // Number of divisions in the grid primaryColor: savedTheme === "dark" ? "#131313" : "#d5d5d5", // Primary color of the grid secondaryColor: savedTheme === "dark" ? "#434343" : "#e3e3e3", // Secondary color of the grid @@ -251,13 +239,25 @@ export const gridConfig: GridConfig = { position3D: [0, -0.5, 0], // Position of the grid in 3D view }; +export const threeDimension: ThreeDimension = { + defaultPosition: [0, 40, 30], // Default position of the camera + defaultTarget: [0, 0, 0], // Default target of the camera + defaultRotation: [0, 0, 0], // Default rotation of the camera + defaultAzimuth: 0, // Default azimuth of the camera + boundaryBottom: [-gridConfig.size / 2, 0, -gridConfig.size / 2], // Bottom boundary of the camera movement + boundaryTop: [gridConfig.size / 2, 100, gridConfig.size / 2], // Top boundary of the camera movement + minDistance: 1, // Minimum distance from the target + leftMouse: 2, // Mouse button for panning + rightMouse: 1, // Mouse button for rotation +}; + export const planeConfig: PlaneConfig = { position2D: [0, -0.5, 0], // Position of the plane position3D: [0, -0.65, 0], // Position of the plane rotation: -Math.PI / 2, // Rotation of the plane - width: 300, // Width of the plane - height: 300, // Height of the plane + width: 150, // Width of the plane + height: 150, // Height of the plane color: savedTheme === "dark" ? "#323232" : "#f3f3f3", // Color of the plane }; diff --git a/app/src/types/world/worldTypes.d.ts b/app/src/types/world/worldTypes.d.ts index c148038..a84eb5e 100644 --- a/app/src/types/world/worldTypes.d.ts +++ b/app/src/types/world/worldTypes.d.ts @@ -217,28 +217,14 @@ export type FloorItems = Array; // Dispatch type for setting floor item state in React export type setFloorItemSetState = React.Dispatch>; -/** Asset Configuration for Loading and Positioning **/ - -// Configuration for assets, allowing model URLs, scaling, positioning, and types -interface AssetConfiguration { - modelUrl: string; - scale?: [number, number, number]; - csgscale?: [number, number, number]; - csgposition?: [number, number, number]; - positionY?: (intersectionPoint: { point: THREE.Vector3 }) => number; - type?: "Fixed-Move" | "Free-Move"; -} - -// Collection of asset configurations, keyed by unique identifiers -export type AssetConfigurations = { [key: string]: AssetConfiguration; }; - /** Wall Item Configuration **/ // Configuration for wall items, including model, scale, position, and rotation interface WallItem { - type: "Fixed-Move" | "Free-Move" | undefined; + type: "fixed-move" | "free-move" | undefined; model?: THREE.Group; modelUuid?: string; + modelfileID: string; modelName?: string; scale?: [number, number, number]; csgscale?: [number, number, number]; diff --git a/app/src/utils/shortcutkeys/handleShortcutKeys.ts b/app/src/utils/shortcutkeys/handleShortcutKeys.ts index c8d735a..813d2cb 100644 --- a/app/src/utils/shortcutkeys/handleShortcutKeys.ts +++ b/app/src/utils/shortcutkeys/handleShortcutKeys.ts @@ -7,9 +7,10 @@ import { useAddAction, useDeleteTool, useSelectedWallItem, + useShortcutStore, useToggleView, useToolMode, -} from "../../store/store"; +} from "../../store/builder/store"; import { usePlayButtonStore } from "../../store/usePlayButtonStore"; import { detectModifierKeys } from "./detectModifierKeys"; import { useSelectedZoneStore } from "../../store/visualization/useZoneStore"; @@ -27,6 +28,7 @@ const KeyPressListener: React.FC = () => { const { setSelectedWallItem } = useSelectedWallItem(); const { setActiveTool } = useActiveTool(); const { clearSelectedZone } = useSelectedZoneStore(); + const { showShortcuts, setShowShortcuts } = useShortcutStore(); const isTextInput = (element: Element | null): boolean => element instanceof HTMLInputElement || @@ -170,10 +172,15 @@ const KeyPressListener: React.FC = () => { setActiveSubTool("cursor"); setIsPlaying(false); clearSelectedZone(); + setShowShortcuts(false); + } + + if (keyCombination === "Ctrl+Shift+?") { + setShowShortcuts(!showShortcuts); } // Placeholder for future implementation - if (["Ctrl+Z", "Ctrl+Y", "Ctrl+Shift+Z", "Ctrl+H", "Ctrl+F", "Ctrl+?"].includes(keyCombination)) { + if (["Ctrl+Z", "Ctrl+Y", "Ctrl+Shift+Z", "Ctrl+H", "Ctrl+F"].includes(keyCombination)) { // Implement undo/redo/help/find/shortcuts } }; @@ -182,7 +189,7 @@ const KeyPressListener: React.FC = () => { window.addEventListener("keydown", handleKeyPress); return () => window.removeEventListener("keydown", handleKeyPress); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [activeModule, toggleUIRight, toggleUILeft, toggleView]); + }, [activeModule, toggleUIRight, toggleUILeft, toggleView, showShortcuts]); return null; };