diff --git a/app/src/assets/textures/floor/black.png b/app/src/assets/textures/floor/black.png new file mode 100644 index 0000000..5562e8a Binary files /dev/null and b/app/src/assets/textures/floor/black.png differ diff --git a/app/src/assets/textures/floor/factory wall texture.jpg b/app/src/assets/textures/floor/factory wall texture.jpg new file mode 100644 index 0000000..65e39c3 Binary files /dev/null and b/app/src/assets/textures/floor/factory wall texture.jpg differ diff --git a/app/src/assets/textures/floor/wall-tex.png b/app/src/assets/textures/floor/wall-tex.png new file mode 100644 index 0000000..d1a09a8 Binary files /dev/null and b/app/src/assets/textures/floor/wall-tex.png differ diff --git a/app/src/assets/textures/floor/white.png b/app/src/assets/textures/floor/white.png new file mode 100644 index 0000000..efc1ea4 Binary files /dev/null and b/app/src/assets/textures/floor/white.png differ diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index b863500..dd2c2a2 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -1,9 +1,6 @@ import React, { useEffect, useState } from "react"; import Search from "../../ui/inputs/Search"; import { getCategoryAsset } from "../../../services/factoryBuilder/assest/assets/getCategoryAsset"; -import arch from "../../../assets/gltf-glb/arch.glb"; -import door from "../../../assets/gltf-glb/door.glb"; -import window from "../../../assets/gltf-glb/window.glb"; import { fetchAssets } from "../../../services/marketplace/fetchAssets"; import { useSelectedItem } from "../../../store/store"; @@ -16,8 +13,6 @@ import storage from "../../../assets/image/categories/storage.png"; import office from "../../../assets/image/categories/office.png"; import safety from "../../../assets/image/categories/safety.png"; import feneration from "../../../assets/image/categories/feneration.png"; -import archThumbnail from "../../../assets/image/localAssets/arch.png"; -import windowThumbnail from "../../../assets/image/localAssets/window.png"; import SkeletonUI from "../../templates/SkeletonUI"; // ------------------------------------- @@ -90,24 +85,7 @@ const Assets: React.FC = () => { useEffect(() => { setCategoryList([ - { - assetName: "Doors", - assetImage: "", - category: "Feneration", - categoryImage: feneration, - }, - { - assetName: "Windows", - assetImage: "", - category: "Feneration", - categoryImage: feneration, - }, - { - assetName: "Pillars", - assetImage: "", - category: "Feneration", - categoryImage: feneration, - }, + { category: "Fenestration", categoryImage: feneration }, { category: "Vehicles", categoryImage: vehicle }, { category: "Workstation", categoryImage: workStation }, { category: "Machines", categoryImage: machines }, @@ -121,44 +99,15 @@ const Assets: React.FC = () => { const fetchCategoryAssets = async (asset: any) => { setisLoading(true); setSelectedCategory(asset); - if (asset === "Feneration") { - const localAssets: AssetProp[] = [ - { - filename: "arch", - category: "Feneration", - url: arch, - thumbnail: archThumbnail, - tags: "arch", - }, - { - filename: "door", - category: "Feneration", - url: door, - thumbnail: feneration, - tags: "door", - }, - { - filename: "window", - category: "Feneration", - url: window, - thumbnail: windowThumbnail, - tags: "window", - }, - ]; - setCategoryAssets(localAssets); - setFiltereredAssets(localAssets); + try { + const res = await getCategoryAsset(asset); + setCategoryAssets(res); + setFiltereredAssets(res); + setisLoading(false); // End loading + // eslint-disable-next-line + } catch (error) { + echo.error("failed to fetch assets"); setisLoading(false); - } else { - try { - const res = await getCategoryAsset(asset); - setCategoryAssets(res); - setFiltereredAssets(res); - setisLoading(false); // End loading - // eslint-disable-next-line - } catch (error) { - echo.error("failed to fetch assets"); - setisLoading(false); - } } }; @@ -167,64 +116,21 @@ const Assets: React.FC = () => {
- {isLoading ? ( - // Show skeleton when loading - ) : searchValue ? ( -
-
-
-

Results for {searchValue}

-
-
- {categoryAssets?.map((asset: any, index: number) => ( -
- {asset.filename} - -
- {asset.filename - .split("_") - .map( - (word: any) => - word.charAt(0).toUpperCase() + word.slice(1) - ) - .join(" ")} -
+ {(() => { + if (isLoading) { + return ; // Show skeleton when loading + } + if (searchValue) { + return ( +
+
+
+

Results for {searchValue}

- ))} -
-
-
- ) : ( - <> - {selectedCategory ? ( -
-

- {selectedCategory}{" "} -
{ - setSelectedCategory(null); - setCategoryAssets([]); - }} - > - ← Back -
-

-
- {categoryAssets && - categoryAssets?.map((asset: any, index: number) => ( +
+ {categoryAssets?.map((asset: any, index: number) => (
{ alt={asset.filename} className="asset-image" onPointerDown={() => { - setSelectedItem({ - name: asset.filename, - id: asset.AssetID, - type: - asset.type === "undefined" - ? undefined - : asset.type, - }); + if (asset.category !== 'Feneration') { + setSelectedItem({ + name: asset.filename, + id: asset.AssetID, + type: + asset.type === "undefined" + ? undefined + : asset.type, + }); + } }} /> +
{asset.filename .split("_") @@ -255,40 +164,104 @@ const Assets: React.FC = () => {
))} +
- ) : ( + ); + } + + if (selectedCategory) { + return (
-

Categories

-
- {Array.from( - new Set(categoryList.map((asset) => asset.category)) - ).map((category, index) => { - const categoryInfo = categoryList.find( - (asset) => asset.category === category - ); - return ( -
fetchCategoryAssets(category)} - > - {category} -
{category}
+

+ {selectedCategory} + +

+
+ {categoryAssets?.map((asset: any, index: number) => ( +
+ {asset.filename} { + setSelectedItem({ + name: asset.filename, + id: asset.AssetID, + type: + asset.type === "undefined" + ? undefined + : asset.type, + }); + }} + /> +
+ {asset.filename + .split("_") + .map( + (word: any) => + word.charAt(0).toUpperCase() + word.slice(1) + ) + .join(" ")}
- ); - })} +
+ ))} + {categoryAssets.length === 0 && ( +
+ 🚧 The asset shelf is empty. We're working on filling it + up! +
+ )}
- )} - - )} + ); + } + + return ( +
+

Categories

+
+ {Array.from( + new Set(categoryList.map((asset) => asset.category)) + ).map((category, index) => { + const categoryInfo = categoryList.find( + (asset) => asset.category === category + ); + return ( + + ); + })} +
+
+ ); + })()}
diff --git a/app/src/components/layout/sidebarLeft/Header.tsx b/app/src/components/layout/sidebarLeft/Header.tsx index 70668af..f26f4fa 100644 --- a/app/src/components/layout/sidebarLeft/Header.tsx +++ b/app/src/components/layout/sidebarLeft/Header.tsx @@ -6,7 +6,7 @@ import useToggleStore from "../../../store/useUIToggleStore"; import useModuleStore from "../../../store/useModuleStore"; const Header: React.FC = () => { - const { toggleUI, setToggleUI } = useToggleStore(); + const { toggleUILeft, toggleUIRight, setToggleUI } = useToggleStore(); const { activeModule } = useModuleStore(); return ( @@ -20,15 +20,17 @@ const Header: React.FC = () => { diff --git a/app/src/components/layout/sidebarLeft/SideBarLeft.tsx b/app/src/components/layout/sidebarLeft/SideBarLeft.tsx index 0765b95..eb618ec 100644 --- a/app/src/components/layout/sidebarLeft/SideBarLeft.tsx +++ b/app/src/components/layout/sidebarLeft/SideBarLeft.tsx @@ -12,7 +12,7 @@ import Search from "../../ui/inputs/Search"; const SideBarLeft: React.FC = () => { const [activeOption, setActiveOption] = useState("Widgets"); - const { toggleUI } = useToggleStore(); + const { toggleUILeft } = useToggleStore(); const { activeModule } = useModuleStore(); // Reset activeOption whenever activeModule changes @@ -31,47 +31,55 @@ const SideBarLeft: React.FC = () => { }; return ( -
+
- {toggleUI && ( + {toggleUILeft && (
- {activeModule === "visualization" ? ( - <> - - -
- {activeOption === "Widgets" ? : } -
- - ) : activeModule === "market" ? ( - <> - ) : activeModule === "builder" ? ( - <> - -
- {activeOption === "Outline" ? : } -
- - ) : ( - <> - -
- {activeOption === "Outline" ? : } -
- - )} + {(() => { + if (activeModule === "visualization") { + return ( + <> + + +
+ {activeOption === "Widgets" ? : } +
+ + ); + } else if (activeModule === "market") { + return <>; + } else if (activeModule === "builder") { + return ( + <> + +
+ {activeOption === "Outline" ? : } +
+ + ); + } else { + return ( + <> + +
+ {activeOption === "Outline" ? : } +
+ + ); + } + })()}
)}
diff --git a/app/src/components/layout/sidebarRight/Header.tsx b/app/src/components/layout/sidebarRight/Header.tsx index 3684157..c7c3070 100644 --- a/app/src/components/layout/sidebarRight/Header.tsx +++ b/app/src/components/layout/sidebarRight/Header.tsx @@ -5,10 +5,15 @@ import { ActiveUser } from "../../../types/users"; import CollaborationPopup from "../../templates/CollaborationPopup"; import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor"; import { useSelectedUserStore } from "../../../store/useCollabStore"; +import useToggleStore from "../../../store/useUIToggleStore"; +import { ToggleSidebarIcon } from "../../icons/HeaderIcons"; +import useModuleStore from "../../../store/useModuleStore"; const Header: React.FC = () => { const { activeUsers } = useActiveUsers(); const userName = localStorage.getItem("userName") ?? "Anonymous"; + const { toggleUILeft, toggleUIRight, setToggleUI } = useToggleStore(); + const { activeModule } = useModuleStore(); const guestUsers: ActiveUser[] = activeUsers.filter( (user: ActiveUser) => user.userName !== userName @@ -55,6 +60,25 @@ const Header: React.FC = () => { )}
+
)} {/* process builder */} - {toggleUI && + {toggleUIRight && subModule === "properties" && activeModule !== "visualization" && !selectedFloorItem && ( @@ -114,7 +114,7 @@ const SideBarRight: React.FC = () => {
)} - {toggleUI && + {toggleUIRight && subModule === "properties" && activeModule !== "visualization" && selectedFloorItem && ( @@ -124,7 +124,7 @@ const SideBarRight: React.FC = () => { )} - {toggleUI && + {toggleUIRight && subModule === "zoneProperties" && (activeModule === "builder" || activeModule === "simulation") && (
@@ -134,7 +134,7 @@ const SideBarRight: React.FC = () => {
)} {/* simulation */} - {toggleUI && activeModule === "simulation" && ( + {toggleUIRight && activeModule === "simulation" && ( <> {subModule === "simulations" && (
@@ -160,7 +160,7 @@ const SideBarRight: React.FC = () => { )} {/* realtime visualization */} - {toggleUI && activeModule === "visualization" && } + {toggleUIRight && activeModule === "visualization" && }
); }; diff --git a/app/src/components/templates/SkeletonUI.tsx b/app/src/components/templates/SkeletonUI.tsx index 76c3fbd..2928916 100644 --- a/app/src/components/templates/SkeletonUI.tsx +++ b/app/src/components/templates/SkeletonUI.tsx @@ -7,7 +7,6 @@ interface SkeletonUIProps { // Define the SkeletonUI component const SkeletonUI: React.FC = ({ type }) => { - // Function to render skeleton content based on 'type' const renderSkeleton = () => { switch (type) { @@ -38,17 +37,28 @@ const SkeletonUI: React.FC = ({ type }) => { case "asset": return ( <> -
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
); - + default: return (
diff --git a/app/src/components/ui/FileMenu.tsx b/app/src/components/ui/FileMenu.tsx index 9997ee4..4ad40c5 100644 --- a/app/src/components/ui/FileMenu.tsx +++ b/app/src/components/ui/FileMenu.tsx @@ -32,7 +32,7 @@ const FileMenu: React.FC = () => { }, []); // project - const [projectName, setProjectName] = useState("project 1"); + const [projectName, setProjectName] = useState("Demo Project"); // Load project name from localStorage on mount useEffect(() => { diff --git a/app/src/components/ui/ModuleToggle.tsx b/app/src/components/ui/ModuleToggle.tsx index a1583e1..4a1532f 100644 --- a/app/src/components/ui/ModuleToggle.tsx +++ b/app/src/components/ui/ModuleToggle.tsx @@ -19,8 +19,11 @@ const ModuleToggle: React.FC = () => { onClick={() => { setActiveModule("builder"); setToggleUI( - localStorage.getItem("navBarUi") - ? localStorage.getItem("navBarUi") === "true" + localStorage.getItem("navBarUiLeft") + ? localStorage.getItem("navBarUiLeft") === "true" + : true, + localStorage.getItem("navBarUiRight") + ? localStorage.getItem("navBarUiRight") === "true" : true ); }} @@ -37,8 +40,11 @@ const ModuleToggle: React.FC = () => { onClick={() => { setActiveModule("simulation"); setToggleUI( - localStorage.getItem("navBarUi") - ? localStorage.getItem("navBarUi") === "true" + localStorage.getItem("navBarUiLeft") + ? localStorage.getItem("navBarUiLeft") === "true" + : true, + localStorage.getItem("navBarUiRight") + ? localStorage.getItem("navBarUiRight") === "true" : true ); }} @@ -55,8 +61,11 @@ const ModuleToggle: React.FC = () => { onClick={() => { setActiveModule("visualization"); setToggleUI( - localStorage.getItem("navBarUi") - ? localStorage.getItem("navBarUi") === "true" + localStorage.getItem("navBarUiLeft") + ? localStorage.getItem("navBarUiLeft") === "true" + : true, + localStorage.getItem("navBarUiRight") + ? localStorage.getItem("navBarUiRight") === "true" : true ); }} @@ -70,7 +79,7 @@ const ModuleToggle: React.FC = () => { className={`module-list ${activeModule === "market" ? "active" : ""}`} onClick={() => { setActiveModule("market"); - setToggleUI(false); + setToggleUI(false, false); }} >
diff --git a/app/src/components/ui/Tools.tsx b/app/src/components/ui/Tools.tsx index 2187fdc..cf77f9e 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -29,7 +29,6 @@ import { useSocketStore, useToggleView, useToolMode, - useTransformMode, useActiveSubTool, } from "../../store/store"; import useToggleStore from "../../store/useUIToggleStore"; @@ -61,7 +60,6 @@ const Tools: React.FC = () => { const { setAddAction } = useAddAction(); const { setSelectedWallItem } = useSelectedWallItem(); - const { setTransformMode } = useTransformMode(); const { setDeletePointOrLine } = useDeletePointOrLine(); const { setToolMode } = useToolMode(); const { activeTool, setActiveTool } = useActiveTool(); @@ -70,8 +68,11 @@ const Tools: React.FC = () => { // Reset activeTool whenever activeModule changes useEffect(() => { setToggleUI( - localStorage.getItem("navBarUi") - ? localStorage.getItem("navBarUi") === "true" + localStorage.getItem("navBarUiLeft") + ? localStorage.getItem("navBarUiLeft") === "true" + : true, + localStorage.getItem("navBarUiRight") + ? localStorage.getItem("navBarUiRight") === "true" : true ); }, []); @@ -92,8 +93,11 @@ const Tools: React.FC = () => { setToggleView(false); } setToggleUI( - localStorage.getItem("navBarUi") - ? localStorage.getItem("navBarUi") === "true" + localStorage.getItem("navBarUiLeft") + ? localStorage.getItem("navBarUiLeft") === "true" + : true, + localStorage.getItem("navBarUiRight") + ? localStorage.getItem("navBarUiRight") === "true" : true ); setToggleThreeD(!toggleThreeD); @@ -118,7 +122,7 @@ const Tools: React.FC = () => { }, []); useEffect(() => { if (!toggleThreeD) { - setToggleUI(false); + setToggleUI(false, false); } }, [toggleThreeD]); @@ -126,7 +130,6 @@ const Tools: React.FC = () => { setToolMode(null); setDeleteTool(false); setAddAction(null); - setTransformMode(null); setDeletePointOrLine(false); setRefTextUpdate((prevUpdate) => prevUpdate - 1); @@ -134,20 +137,6 @@ const Tools: React.FC = () => { case "cursor": if (toggleView) { setToolMode('move'); - } else { - setTransformMode("translate"); - } - break; - - case "Rotate": - if (!toggleView) { - setTransformMode("rotate"); - } - break; - - case "Scale": - if (!toggleView) { - setTransformMode("scale"); } break; @@ -208,8 +197,9 @@ const Tools: React.FC = () => {
{activeSubTool == "cursor" && (
{ setActiveTool("cursor"); }} @@ -220,8 +210,9 @@ const Tools: React.FC = () => { )} {activeSubTool == "free-hand" && (
{ setActiveTool("free-hand"); }} @@ -232,8 +223,9 @@ const Tools: React.FC = () => { )} {activeSubTool == "delete" && (
{ setActiveTool("delete"); }} @@ -306,8 +298,9 @@ const Tools: React.FC = () => {
{ setActiveTool("draw-wall"); }} @@ -316,8 +309,9 @@ const Tools: React.FC = () => {
{ setActiveTool("draw-zone"); }} @@ -326,8 +320,9 @@ const Tools: React.FC = () => {
{ setActiveTool("draw-aisle"); }} @@ -336,8 +331,9 @@ const Tools: React.FC = () => {
{ setActiveTool("draw-floor"); }} @@ -353,8 +349,9 @@ const Tools: React.FC = () => {
{ setActiveTool("measure"); }} @@ -370,8 +367,9 @@ const Tools: React.FC = () => {
{ setActiveTool("pen"); }} @@ -408,8 +406,9 @@ const Tools: React.FC = () => {
{ setActiveTool("comment"); }} @@ -419,8 +418,9 @@ const Tools: React.FC = () => {
{toggleThreeD && (
{ setIsPlaying(!isPlaying); }} @@ -434,8 +434,9 @@ const Tools: React.FC = () => { <>
toggle view (tab)
diff --git a/app/src/components/ui/analysis/ROISummary.tsx b/app/src/components/ui/analysis/ROISummary.tsx index 0c4d80e..e0edb74 100644 --- a/app/src/components/ui/analysis/ROISummary.tsx +++ b/app/src/components/ui/analysis/ROISummary.tsx @@ -205,7 +205,7 @@ const ROISummary = ({
-
+ {/*
@@ -224,7 +224,7 @@ const ROISummary = ({ -
+
*/} ) : ( diff --git a/app/src/components/ui/analysis/ThroughputSummary.tsx b/app/src/components/ui/analysis/ThroughputSummary.tsx index 82116cf..8abdf22 100644 --- a/app/src/components/ui/analysis/ThroughputSummary.tsx +++ b/app/src/components/ui/analysis/ThroughputSummary.tsx @@ -5,7 +5,7 @@ import SkeletonUI from "../../templates/SkeletonUI"; const ProductionCapacity = ({ progressPercent = 50, - avgProcessTime = "28.4 Secs/unit", + avgProcessTime = "28.4", machineUtilization = "78%", throughputValue = 128, timeRange = { startTime: "08:00 AM", endTime: "09:00 AM" }, @@ -34,7 +34,7 @@ const ProductionCapacity = ({ <>
- {throughputValue} Units/hour + {avgProcessTime} secs/unit
{/* Dynamic Progress Bar */} @@ -56,8 +56,8 @@ const ProductionCapacity = ({
- Avg. Process Time - {avgProcessTime} + Units/hour + {throughputValue} avg.
Machine Utilization diff --git a/app/src/components/ui/menu/menu.tsx b/app/src/components/ui/menu/menu.tsx index 39508b6..e784808 100644 --- a/app/src/components/ui/menu/menu.tsx +++ b/app/src/components/ui/menu/menu.tsx @@ -1,12 +1,14 @@ import React, { useState } from "react"; import { ArrowIcon } from "../../icons/ExportCommonIcons"; import { toggleTheme } from "../../../utils/theme"; +import { useNavigate } from "react-router-dom"; interface MenuBarProps { setOpenMenu: (isOpen: boolean) => void; // Function to update menu state } const MenuBar: React.FC = ({ setOpenMenu }) => { + const navigate = useNavigate(); const [activeMenu, setActiveMenu] = useState(null); const [activeSubMenu, setActiveSubMenu] = useState(null); @@ -23,13 +25,19 @@ const MenuBar: React.FC = ({ setOpenMenu }) => { })); }; - function handleThemeChange(){ + function handleThemeChange() { toggleTheme(); window.location.reload(); } const savedTheme: string | null = localStorage.getItem("theme") ?? "light"; + + const handleLogout = () => { + localStorage.clear(); // 1. Clear all localStorage + navigate('/'); // 2. Redirect to homepage + }; + return (
= ({ setOpenMenu }) => {
)}
+
+
Log out
+
); diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx index dfae129..f43cf3d 100644 --- a/app/src/modules/builder/builder.tsx +++ b/app/src/modules/builder/builder.tsx @@ -54,57 +54,57 @@ import NavMesh from "../simulation/vehicle/navMesh/navMesh"; import CalculateAreaGroup from "./groups/calculateAreaGroup"; 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 +120,39 @@ 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 ////////// + ////////// 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", - }, - }; + const AssetConfigurations: Types.AssetConfigurations = { + arch: { + modelUrl: arch, + scale: [0.75, 0.75, 0.75], + csgscale: [2, 4, 0.5], + csgposition: [0, 2, 0], + type: "Fixed-Move", + }, + door: { + modelUrl: door, + scale: [0.75, 0.75, 0.75], + csgscale: [2, 4, 0.5], + csgposition: [0, 2, 0], + type: "Fixed-Move", + }, + window: { + modelUrl: Window, + scale: [0.75, 0.75, 0.75], + csgscale: [5, 3, 0.5], + csgposition: [0, 1.5, 0], + type: "Free-Move", + }, + }; - ////////// All Toggle's ////////// + ////////// All Toggle's ////////// useEffect(() => { setRefTextUpdate((prevUpdate: number) => prevUpdate - 1); @@ -177,167 +174,167 @@ 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..eab8edc 100644 --- a/app/src/modules/builder/csg/csg.tsx +++ b/app/src/modules/builder/csg/csg.tsx @@ -4,51 +4,51 @@ import { useDeleteTool } from "../../../store/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; - }; + props.hoveredDeletableWallItem.current = hovered ? object : 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/geomentries/floors/addFloorToScene.ts b/app/src/modules/builder/geomentries/floors/addFloorToScene.ts index c951ba0..71f2d24 100644 --- a/app/src/modules/builder/geomentries/floors/addFloorToScene.ts +++ b/app/src/modules/builder/geomentries/floors/addFloorToScene.ts @@ -2,8 +2,8 @@ import * as THREE from 'three'; import * as Types from "../../../../types/world/worldTypes"; import * as CONSTANTS from "../../../../types/world/worldConstants"; -import texturePath from "../../../../assets/textures/floor/concreteFloorWorn001Diff2k.jpg"; -import normalPath from "../../../../assets/textures/floor/concreteFloorWorn001NorGl2k.jpg"; +import texturePath from "../../../../assets/textures/floor/white.png"; +import texturePathDark from "../../../../assets/textures/floor/black.png"; // Cache for materials const materialCache = new Map(); @@ -14,6 +14,8 @@ export default function addFloorToScene( floorGroup: Types.RefGroup, userData: any, ) { + const savedTheme: string | null = localStorage.getItem('theme'); + const textureLoader = new THREE.TextureLoader(); const textureScale = CONSTANTS.floorConfig.textureScale; @@ -25,19 +27,15 @@ export default function addFloorToScene( if (materialCache.has(materialKey)) { material = materialCache.get(materialKey) as THREE.Material; } else { - const floorTexture = textureLoader.load(texturePath); - const normalMap = textureLoader.load(normalPath); + const floorTexture = textureLoader.load(savedTheme === "dark" ? texturePathDark : texturePath); + // const floorTexture = textureLoader.load(texturePath); floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping; floorTexture.repeat.set(textureScale, textureScale); floorTexture.colorSpace = THREE.SRGBColorSpace; - normalMap.wrapS = normalMap.wrapT = THREE.RepeatWrapping; - normalMap.repeat.set(textureScale, textureScale); - material = new THREE.MeshStandardMaterial({ map: floorTexture, - normalMap: normalMap, side: THREE.DoubleSide, }); diff --git a/app/src/modules/builder/geomentries/walls/addWallItems.ts b/app/src/modules/builder/geomentries/walls/addWallItems.ts index fd9eb48..8b472ec 100644 --- a/app/src/modules/builder/geomentries/walls/addWallItems.ts +++ b/app/src/modules/builder/geomentries/walls/addWallItems.ts @@ -35,7 +35,7 @@ async function AddWallItems( }); const config = AssetConfigurations[selected]; - let positionY = typeof config.positionY === 'function' ? config.positionY(intersectionPoint) : config.positionY; + let positionY = config.type === 'Fixed-Move' ? 0 : intersectionPoint.point.y; if (positionY === 0) { positionY = Math.floor(intersectionPoint.point.y / CONSTANTS.wallConfig.height) * CONSTANTS.wallConfig.height; } diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx index 1ab9d59..e58530b 100644 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ b/app/src/modules/builder/groups/floorItemsGroup.tsx @@ -1,7 +1,6 @@ import { useFrame, useThree } from "@react-three/fiber"; import { useActiveTool, - useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteTool, @@ -12,9 +11,7 @@ import { useSelectedItem, useSocketStore, useToggleView, - useTransformMode, } from "../../../store/store"; -import assetVisibility from "../geomentries/assets/assetVisibility"; import { useEffect } from "react"; import * as THREE from "three"; import * as Types from "../../../types/world/worldTypes"; @@ -29,7 +26,6 @@ import loadInitialFloorItems from "../IntialLoad/loadInitialFloorItems"; import addAssetModel from "../geomentries/assets/addAssetModel"; import { getFloorAssets } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi"; import useModuleStore from "../../../store/useModuleStore"; -// import { retrieveGLTF } from "../../../utils/indexDB/idbUtils"; import { useEventsStore } from "../../../store/simulation/useEventsStore"; const assetManagerWorker = new Worker( @@ -62,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(); @@ -198,9 +193,7 @@ const FloorItemsGroup = ({ }; const startInterval = () => { - if (!intervalId) { - intervalId = setInterval(handleChange, 50); - } + intervalId ??= setInterval(handleChange, 50); }; const stopInterval = () => { @@ -262,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, @@ -301,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, @@ -417,16 +408,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/wallItemsGroup.tsx b/app/src/modules/builder/groups/wallItemsGroup.tsx index 76ba502..faae06d 100644 --- a/app/src/modules/builder/groups/wallItemsGroup.tsx +++ b/app/src/modules/builder/groups/wallItemsGroup.tsx @@ -1,12 +1,12 @@ import { useEffect } from "react"; import { - useDeleteTool, - useDeletePointOrLine, - useObjectPosition, - useObjectRotation, - useSelectedWallItem, - useSocketStore, - useWallItems, + useDeleteTool, + useDeletePointOrLine, + useObjectPosition, + useObjectRotation, + useSelectedWallItem, + useSocketStore, + useWallItems, } from "../../../store/store"; import { Csg } from "../csg/csg"; import * as Types from "../../../types/world/worldTypes"; @@ -20,276 +20,276 @@ import AddWallItems from "../geomentries/walls/addWallItems"; import useModuleStore from "../../../store/useModuleStore"; const WallItemsGroup = ({ - currentWallItem, - AssetConfigurations, - hoveredDeletableWallItem, - selectedItemsIndex, - setSelectedItemsIndex, - CSGGroup, + currentWallItem, + AssetConfigurations, + 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(); - useEffect(() => { - // Load Wall Items from the backend - loadInitialWallItems(setWallItems, AssetConfigurations); - }, []); + useEffect(() => { + // Load Wall Items from the backend + loadInitialWallItems(setWallItems, AssetConfigurations); + }, []); - ////////// Update the Scale value changes in thewallItems State ////////// + ////////// 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 ////////// - ////////// Update the Rotation value changes in the selected item ////////// + ////////// Update the Rotation value changes in the selected item ////////// - useEffect(() => { - const canvasElement = state.gl.domElement; - function handlePointerMove(e: any) { - if ( - selectedItemsIndex !== null && - !deletePointOrLine && - e.buttons === 1 - ) { - const Raycaster = state.raycaster; - const intersects = Raycaster.intersectObjects( - CSGGroup.current?.children[0].children!, - true - ); - const Object = intersects.find((child) => - child.object.name.includes("WallRaycastReference") - ); + useEffect(() => { + const canvasElement = state.gl.domElement; + function handlePointerMove(e: any) { + if ( + selectedItemsIndex !== null && + !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 (Object) { - (state.controls as any)!.enabled = false; - setWallItems((prevItems: any) => { - const updatedItems = [...prevItems]; - let position: [number, number, number] = [0, 0, 0]; + if (Object) { + (state.controls as any)!.enabled = false; + setWallItems((prevItems: any) => { + const updatedItems = [...prevItems]; + let position: [number, number, number] = [0, 0, 0]; - if (updatedItems[selectedItemsIndex].type === "Fixed-Move") { - position = [ - Object!.point.x, - Math.floor(Object!.point.y / CONSTANTS.wallConfig.height) * - CONSTANTS.wallConfig.height, - Object!.point.z, - ]; - } else if (updatedItems[selectedItemsIndex].type === "Free-Move") { - position = [Object!.point.x, Object!.point.y, Object!.point.z]; + if (updatedItems[selectedItemsIndex].type === "Fixed-Move") { + position = [ + Object!.point.x, + Math.floor(Object!.point.y / CONSTANTS.wallConfig.height) * + CONSTANTS.wallConfig.height, + Object!.point.z, + ]; + } else if (updatedItems[selectedItemsIndex].type === "Free-Move") { + position = [Object!.point.x, Object!.point.y, Object!.point.z]; + } + + requestAnimationFrame(() => { + setObjectPosition(new THREE.Vector3(...position)); + setObjectRotation({ + x: THREE.MathUtils.radToDeg(Object!.object.rotation.x), + y: THREE.MathUtils.radToDeg(Object!.object.rotation.y), + z: THREE.MathUtils.radToDeg(Object!.object.rotation.z), + }); + }); + + 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.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!, + 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 (!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]); - 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 6b6c97d..19d5833 100644 --- a/app/src/modules/builder/groups/wallsAndWallItems.tsx +++ b/app/src/modules/builder/groups/wallsAndWallItems.tsx @@ -1,29 +1,32 @@ import { Geometry } from "@react-three/csg"; -import { useDeleteTool, useSelectedWallItem, useToggleView, useTransformMode, useWallItems, useWalls } from "../../../store/store"; +import { + useDeleteTool, + useSelectedWallItem, + useToggleView, + useWallItems, + useWalls, +} from "../../../store/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 }: any) => { - const { walls, setWalls } = useWalls(); - const { wallItems, setWallItems } = useWallItems(); - const { toggleView, setToggleView } = useToggleView(); - const { deleteTool, setDeleteTool } = useDeleteTool(); - const { transformMode, setTransformMode } = useTransformMode(); - const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem(); - - useEffect(() => { - if (transformMode === null) { - if (!deleteTool) { - handleMeshMissed(currentWallItem, setSelectedWallItem, setSelectedItemsIndex); - setSelectedWallItem(null); - setSelectedItemsIndex(null); - } - } - }, [transformMode]) +const WallsAndWallItems = ({ + CSGGroup, + AssetConfigurations, + setSelectedItemsIndex, + selectedItemsIndex, + currentWallItem, + csg, + lines, + hoveredDeletableWallItem, +}: any) => { + const { walls } = useWalls(); + const { wallItems } = useWallItems(); + const { toggleView } = useToggleView(); + const { deleteTool } = useDeleteTool(); + const { setSelectedWallItem } = useSelectedWallItem(); return ( { - if (!deleteTool && transformMode !== null) { - handleMeshDown(event, currentWallItem, setSelectedWallItem, setSelectedItemsIndex, wallItems, toggleView); + if (!deleteTool) { + handleMeshDown( + event, + currentWallItem, + setSelectedWallItem, + setSelectedItemsIndex, + wallItems, + toggleView + ); } }} onPointerMissed={() => { if (!deleteTool) { - handleMeshMissed(currentWallItem, setSelectedWallItem, setSelectedItemsIndex); + handleMeshMissed( + currentWallItem, + setSelectedWallItem, + setSelectedItemsIndex + ); setSelectedWallItem(null); setSelectedItemsIndex(null); } @@ -47,10 +61,17 @@ const WallsAndWallItems = ({ CSGGroup, AssetConfigurations, setSelectedItemsInde > - + - ) -} + ); +}; -export default WallsAndWallItems; \ No newline at end of file +export default WallsAndWallItems; diff --git a/app/src/modules/builder/groups/wallsMesh.tsx b/app/src/modules/builder/groups/wallsMesh.tsx index 92a0ee3..9abf59d 100644 --- a/app/src/modules/builder/groups/wallsMesh.tsx +++ b/app/src/modules/builder/groups/wallsMesh.tsx @@ -1,23 +1,27 @@ -import * as THREE from 'three'; -import * as Types from '../../../types/world/worldTypes'; -import * as CONSTANTS from '../../../types/world/worldConstants'; -import { Base } from '@react-three/csg'; -import { MeshDiscardMaterial } from '@react-three/drei'; -import { useUpdateScene, useWalls } from '../../../store/store'; -import { useEffect } from 'react'; -import { getLines } from '../../../services/factoryBuilder/lines/getLinesApi'; -import objectLinesToArray from '../geomentries/lines/lineConvertions/objectLinesToArray'; -import loadWalls from '../geomentries/walls/loadWalls'; +import * as THREE from "three"; +import * as Types from "../../../types/world/worldTypes"; +import * as CONSTANTS from "../../../types/world/worldConstants"; +import { Base } from "@react-three/csg"; +import { MeshDiscardMaterial } from "@react-three/drei"; +import { useUpdateScene, useWalls } from "../../../store/store"; +import React, { useEffect } from "react"; +import { getLines } from "../../../services/factoryBuilder/lines/getLinesApi"; +import objectLinesToArray from "../geomentries/lines/lineConvertions/objectLinesToArray"; +import loadWalls from "../geomentries/walls/loadWalls"; +// texture +import texturePath from "../../../assets/textures/floor/wall-tex.png"; -const WallsMesh = ({ lines }: any) => { +// Cache for materials +const materialCache = new Map(); + +const WallsMeshComponent = ({ lines }: any) => { const { walls, setWalls } = useWalls(); const { updateScene, setUpdateScene } = useUpdateScene(); useEffect(() => { if (updateScene) { - - const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; getLines(organization).then((data) => { const Lines: Types.Lines = objectLinesToArray(data); @@ -26,15 +30,22 @@ const WallsMesh = ({ lines }: any) => { if (Lines) { loadWalls(lines, setWalls); } - }) + }); setUpdateScene(false); } - }, [updateScene]) + }, [updateScene]); + + 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; return ( <> {walls.map((wall: Types.Wall, index: number) => ( - + { { ))} - ) -} + ); +}; -export default WallsMesh; \ No newline at end of file +const WallsMesh = React.memo(WallsMeshComponent); +export default WallsMesh; diff --git a/app/src/modules/scene/controls/controls.tsx b/app/src/modules/scene/controls/controls.tsx index 8bdd3aa..1420d89 100644 --- a/app/src/modules/scene/controls/controls.tsx +++ b/app/src/modules/scene/controls/controls.tsx @@ -10,6 +10,7 @@ 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/distanceFindingControls.tsx b/app/src/modules/scene/controls/selectionControls/distanceFindingControls.tsx index 25e6c96..1cfe244 100644 --- a/app/src/modules/scene/controls/selectionControls/distanceFindingControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/distanceFindingControls.tsx @@ -1,270 +1,270 @@ import React, { useRef } from "react"; import { - Vector3, - Raycaster, - BufferGeometry, - LineBasicMaterial, - Line, - Mesh, - Group, + Vector3, + Raycaster, + BufferGeometry, + LineBasicMaterial, + Line, + Mesh, + Group, } from "three"; import { useThree, useFrame } from "@react-three/fiber"; import { Html } from "@react-three/drei"; interface DistanceFindingControlsProps { - boundingBoxRef: React.RefObject; - object: number; + boundingBoxRef: React.RefObject; + object: number; } const DistanceFindingControls = ({ - boundingBoxRef, - object, + boundingBoxRef, + object, }: DistanceFindingControlsProps) => { - const { camera, scene } = useThree(); + const { camera, scene } = useThree(); - // Refs for measurement lines - const line1 = useRef(null); - const line2 = useRef(null); - const line3 = useRef(null); - const line4 = useRef(null); - const line5 = useRef(null); + // Refs for measurement lines + const line1 = useRef(null); + const line2 = useRef(null); + const line3 = useRef(null); + const line4 = useRef(null); + const line5 = useRef(null); - // Refs for measurement text labels - const textPosX = useRef(null); - const textNegX = useRef(null); - const textPosZ = useRef(null); - const textNegZ = useRef(null); - const textPosY = useRef(null); + // Refs for measurement text labels + const textPosX = useRef(null); + const textNegX = useRef(null); + const textPosZ = useRef(null); + const textNegZ = useRef(null); + const textPosY = useRef(null); - // Store line geometries to avoid recreation - const lineGeometries = useRef({ - posX: new BufferGeometry(), - negX: new BufferGeometry(), - posZ: new BufferGeometry(), - negZ: new BufferGeometry(), - posY: new BufferGeometry(), - }); + // Store line geometries to avoid recreation + const lineGeometries = useRef({ + posX: new BufferGeometry(), + negX: new BufferGeometry(), + posZ: new BufferGeometry(), + negZ: new BufferGeometry(), + posY: new BufferGeometry(), + }); - useFrame(() => { - if (!boundingBoxRef?.current) return; + useFrame(() => { + if (!boundingBoxRef?.current) return; - boundingBoxRef.current.geometry.computeBoundingBox(); - const bbox = boundingBoxRef.current.geometry.boundingBox; + boundingBoxRef.current.geometry.computeBoundingBox(); + const bbox = boundingBoxRef.current.geometry.boundingBox; - if (!bbox) return; + if (!bbox) return; - const size = { - x: bbox.max.x - bbox.min.x, - y: bbox.max.y - bbox.min.y, - z: bbox.max.z - bbox.min.z, + const size = { + x: bbox.max.x - bbox.min.x, + y: bbox.max.y - bbox.min.y, + z: bbox.max.z - bbox.min.z, + }; + + const vec = boundingBoxRef.current?.getWorldPosition(new Vector3()).clone(); + + if (!vec) return; + updateLine({ + line: line1.current, + geometry: lineGeometries.current.posX, + direction: new Vector3(1, 0, 0), // Positive X + angle: "pos", + mesh: textPosX, + vec, + size, + }); + updateLine({ + line: line2.current, + geometry: lineGeometries.current.negX, + direction: new Vector3(-1, 0, 0), // Negative X + angle: "neg", + mesh: textNegX, + vec, + size, + }); + updateLine({ + line: line3.current, + geometry: lineGeometries.current.posZ, + direction: new Vector3(0, 0, 1), // Positive Z + angle: "pos", + mesh: textPosZ, + vec, + size, + }); + updateLine({ + line: line4.current, + geometry: lineGeometries.current.negZ, + direction: new Vector3(0, 0, -1), // Negative Z + angle: "neg", + mesh: textNegZ, + vec, + size, + }); + updateLine({ + line: line5.current, + geometry: lineGeometries.current.posY, + direction: new Vector3(0, -1, 0), // Down (Y) + angle: "posY", + mesh: textPosY, + vec, + size, + }); + }); + + const updateLine = ({ + line, + geometry, + direction, + angle, + mesh, + vec, + size, + }: { + line: Line | null; + geometry: BufferGeometry; + direction: Vector3; + angle: string; + mesh: React.RefObject; + vec: Vector3; + size: { x: number; y: number; z: number }; + }) => { + if (!line) return; + + const points = []; + + if (angle === "pos") { + points[0] = new Vector3(vec.x, vec.y, vec.z).add( + new Vector3((direction.x * size.x) / 2, 0, (direction.z * size.z) / 2) + ); + } else if (angle === "neg") { + points[0] = new Vector3(vec.x, vec.y, vec.z).sub( + new Vector3((-direction.x * size.x) / 2, 0, (-direction.z * size.z) / 2) + ); + } else if (angle === "posY") { + points[0] = new Vector3(vec.x, vec.y, vec.z).sub( + new Vector3(0, size.y / 2, 0) + ); + } + + const ray = new Raycaster(); + if (camera) ray.camera = camera; + ray.set(new Vector3(vec.x, vec.y, vec.z), direction); + ray.params.Line.threshold = 0.1; + + // Find intersection points + const wallsGroup = scene.children.find((val) => + val?.name.includes("Walls") + ); + const intersects = wallsGroup + ? ray.intersectObjects([wallsGroup], true) + : []; + + // Find intersection point + if (intersects[0]) { + for (const intersect of intersects) { + if (intersect.object.name.includes("Wall")) { + points[1] = + angle !== "posY" ? intersect.point : new Vector3(vec.x, 0, vec.z); // Floor + break; + } + } + } + + // Update line geometry + if (points[1]) { + geometry.dispose(); + geometry.setFromPoints([points[0], points[1]]); + line.geometry = geometry; + + // Update measurement text + if (mesh?.current) { + geometry.computeBoundingSphere(); + const center = geometry.boundingSphere?.center; + if (center) { + mesh.current.position.copy(center); + } + const label = document.getElementById(mesh.current.name); + if (label) { + label.innerText = `${points[0].distanceTo(points[1]).toFixed(2)}m`; + } + } + } else { + // No intersection found - clear the line + geometry.dispose(); + geometry.setFromPoints([new Vector3(), new Vector3()]); + line.geometry = geometry; + const label = document.getElementById(mesh?.current?.name ?? ""); + if (label) label.innerText = ""; + } }; - const vec = boundingBoxRef.current?.getWorldPosition(new Vector3()).clone(); + const Material = new LineBasicMaterial({ color: "#d2baff" }); - if (!vec) return; - updateLine({ - line: line1.current, - geometry: lineGeometries.current.posX, - direction: new Vector3(1, 0, 0), // Positive X - angle: "pos", - mesh: textPosX, - vec, - size, - }); - updateLine({ - line: line2.current, - geometry: lineGeometries.current.negX, - direction: new Vector3(-1, 0, 0), // Negative X - angle: "neg", - mesh: textNegX, - vec, - size, - }); - updateLine({ - line: line3.current, - geometry: lineGeometries.current.posZ, - direction: new Vector3(0, 0, 1), // Positive Z - angle: "pos", - mesh: textPosZ, - vec, - size, - }); - updateLine({ - line: line4.current, - geometry: lineGeometries.current.negZ, - direction: new Vector3(0, 0, -1), // Negative Z - angle: "neg", - mesh: textNegZ, - vec, - size, - }); - updateLine({ - line: line5.current, - geometry: lineGeometries.current.posY, - direction: new Vector3(0, -1, 0), // Down (Y) - angle: "posY", - mesh: textPosY, - vec, - size, - }); - }); - - const updateLine = ({ - line, - geometry, - direction, - angle, - mesh, - vec, - size, - }: { - line: Line | null; - geometry: BufferGeometry; - direction: Vector3; - angle: string; - mesh: React.RefObject; - vec: Vector3; - size: { x: number; y: number; z: number }; - }) => { - if (!line) return; - - const points = []; - - if (angle === "pos") { - points[0] = new Vector3(vec.x, vec.y, vec.z).add( - new Vector3((direction.x * size.x) / 2, 0, (direction.z * size.z) / 2) - ); - } else if (angle === "neg") { - points[0] = new Vector3(vec.x, vec.y, vec.z).sub( - new Vector3((-direction.x * size.x) / 2, 0, (-direction.z * size.z) / 2) - ); - } else if (angle === "posY") { - points[0] = new Vector3(vec.x, vec.y, vec.z).sub( - new Vector3(0, size.y / 2, 0) - ); - } - - const ray = new Raycaster(); - if (camera) ray.camera = camera; - ray.set(new Vector3(vec.x, vec.y, vec.z), direction); - ray.params.Line.threshold = 0.1; - - // Find intersection points - const wallsGroup = scene.children.find((val) => - val?.name.includes("Walls") - ); - const intersects = wallsGroup - ? ray.intersectObjects([wallsGroup], true) - : []; - - // Find intersection point - if (intersects[0]) { - for (const intersect of intersects) { - if (intersect.object.name.includes("Wall")) { - points[1] = - angle !== "posY" ? intersect.point : new Vector3(vec.x, 0, vec.z); // Floor - break; - } - } - } - - // Update line geometry - if (points[1]) { - geometry.dispose(); - geometry.setFromPoints([points[0], points[1]]); - line.geometry = geometry; - - // Update measurement text - if (mesh?.current) { - geometry.computeBoundingSphere(); - const center = geometry.boundingSphere?.center; - if (center) { - mesh.current.position.copy(center); - } - const label = document.getElementById(mesh.current.name); - if (label) { - label.innerText = `${points[0].distanceTo(points[1]).toFixed(2)}m`; - } - } - } else { - // No intersection found - clear the line - geometry.dispose(); - geometry.setFromPoints([new Vector3(), new Vector3()]); - line.geometry = geometry; - const label = document.getElementById(mesh?.current?.name ?? ""); - if (label) label.innerText = ""; - } - }; - - const Material = new LineBasicMaterial({ color: "#d2baff" }); - - return ( - <> - {/* Measurement text labels */} - {boundingBoxRef.current && object > 0 && ( + return ( <> - - -
- -
- - -
- -
- - -
- -
- - -
- -
+ {/* Measurement text labels */} + {boundingBoxRef.current && object > 0 && ( + <> + + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
- {/* Measurement lines */} - - - - + {/* Measurement lines */} + + + + + + )} - )} - - ); + ); }; export default DistanceFindingControls; diff --git a/app/src/modules/scene/controls/selectionControls/moveControls.tsx b/app/src/modules/scene/controls/selectionControls/moveControls.tsx index 498056a..5398fe6 100644 --- a/app/src/modules/scene/controls/selectionControls/moveControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/moveControls.tsx @@ -2,10 +2,10 @@ import * as THREE from "three"; import { useEffect, useMemo, useRef, useState } from "react"; import { useFrame, useThree } from "@react-three/fiber"; import { - useFloorItems, - useSelectedAssets, - useSocketStore, - useToggleView, + useFloorItems, + useSelectedAssets, + useSocketStore, + useToggleView, } from "../../../../store/store"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { toast } from "react-toastify"; @@ -19,370 +19,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]; - - 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) { - 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); + } + }); + toast.success("Object moved!"); + + itemsData.current = []; + clearSelection(); + }; + + const clearSelection = () => { + selectionGroup.current.children = []; + selectionGroup.current.position.set(0, 0, 0); + selectionGroup.current.rotation.set(0, 0, 0); + setpastedObjects([]); + setDuplicatedObjects([]); + setMovedObjects([]); + setRotatedObjects([]); + setSelectedAssets([]); + 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 58dab0c..31ba8c9 100644 --- a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx @@ -22,7 +22,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo const itemsData = useRef([]); const email = localStorage.getItem('email') - const organization = (email!.split("@")[1]).split(".")[0]; + const organization = (email?.split("@")[1])?.split(".")[0] ?? null; const updateBackend = ( productName: string, @@ -214,7 +214,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], }) - if (event) { + if (event && organization) { updateBackend( selectedProduct.productName, selectedProduct.productId, 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..5311f4f --- /dev/null +++ b/app/src/modules/scene/controls/transformControls/transformControls.tsx @@ -0,0 +1,206 @@ +import { TransformControls } from "@react-three/drei"; +import * as THREE from "three"; +import { useSelectedFloorItem, useObjectPosition, useObjectRotation, useFloorItems, useActiveTool, useSocketStore } from "../../../../store/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); + } 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/visualization/RealTimeVisulization.tsx b/app/src/modules/visualization/RealTimeVisulization.tsx index 23f2532..bbc996f 100644 --- a/app/src/modules/visualization/RealTimeVisulization.tsx +++ b/app/src/modules/visualization/RealTimeVisulization.tsx @@ -187,6 +187,7 @@ const RealTimeVisulization: React.FC = () => {
((set: any) => ({ 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 })), diff --git a/app/src/store/useUIToggleStore.ts b/app/src/store/useUIToggleStore.ts index a508b38..9f5f583 100644 --- a/app/src/store/useUIToggleStore.ts +++ b/app/src/store/useUIToggleStore.ts @@ -1,13 +1,17 @@ import { create } from "zustand"; interface ToggleState { - toggleUI: boolean; // State to track UI toggle - setToggleUI: (value: boolean) => void; // Action to update toggleUI + toggleUILeft: boolean; + toggleUIRight: boolean; + setToggleUI: (value1: boolean, value2: boolean) => void; } const useToggleStore = create((set) => ({ - toggleUI: true, // Initial state - setToggleUI: (value: boolean) => set({ toggleUI: value }), // Update the state + toggleUILeft: true, + toggleUIRight: false, + setToggleUI: (value1: boolean, value2: boolean) => { + set({ toggleUILeft: value1, toggleUIRight: value2 }); + }, })); export default useToggleStore; diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index 4d321d4..e6b8f10 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -1,6 +1,55 @@ @use "../abstracts/variables" as *; @use "../abstracts/mixins" as *; +.toggle-sidebar-ui-button { + @include flex-center; + cursor: pointer; + height: 32px; + width: 32px; + min-height: 32px; + min-width: 32px; + border-radius: #{$border-radius-large}; + position: relative; + + .tooltip { + top: 6px; + right: -168px; + + &::after { + left: 0px; + bottom: 50%; + } + } + + &:hover { + outline: 1px solid var(--border-color); + outline-offset: -1px; + background: var(--background-color-solid); + + .tooltip { + opacity: 1; + transform: translateX(2px); + } + } +} + +.toggle-sidebar-ui-button.active { + background: var(--background-color-accent); + + rect { + stroke: var(--icon-default-color-active); + } + + circle { + fill: var(--icon-default-color-active); + } + + &:hover { + filter: saturate(0.8); + background: var(--background-color-accent); + } +} + .sidebar-left-wrapper { width: 270px; position: fixed; @@ -34,15 +83,6 @@ } .toggle-sidebar-ui-button { - @include flex-center; - cursor: pointer; - height: 32px; - width: 32px; - min-height: 32px; - min-width: 32px; - border-radius: #{$border-radius-large}; - position: relative; - .tooltip { top: 6px; right: -168px; @@ -52,34 +92,6 @@ bottom: 50%; } } - - &:hover { - outline: 1px solid var(--border-color); - outline-offset: -1px; - background: var(--background-color-solid); - - .tooltip { - opacity: 1; - transform: translateX(2px); - } - } - } - - .active { - background: var(--background-color-accent); - - rect { - stroke: var(--icon-default-color-active); - } - - circle { - fill: var(--icon-default-color-active); - } - - &:hover { - filter: saturate(0.8); - background: var(--background-color-accent); - } } } @@ -295,7 +307,7 @@ padding: 10px; padding-left: 16px; width: 100%; - gap: 12px; + gap: 8px; height: 52px; .options-container { @@ -318,7 +330,7 @@ .split { height: 20px; - width: 2px; + min-width: 1px; background: var(--text-disabled); } @@ -1002,8 +1014,7 @@ height: 100%; width: 1px; position: absolute; - color: var(--text-color); - opacity: 0.4; + color: var(--accent-color); font-size: var(--font-size-regular); outline-offset: -1px; top: 0; @@ -1266,6 +1277,18 @@ } } } + .toggle-sidebar-ui-button { + svg { + transform: scaleX(-1); + } + .tooltip { + right: 56px; + &::after { + left: 100%; + bottom: 50%; + } + } + } } .assets-container-main { @@ -1432,7 +1455,11 @@ height: 100%; gap: 6px; padding: 2px; - + .no-asset { + text-align: center; + margin: 12px; + width: 100%; + } .assets { width: 122px; height: 95px; @@ -1491,20 +1518,6 @@ } } -.skeleton-wrapper { - display: flex; - - .asset-name { - width: 40%; - height: 10px; - } - - .asset { - width: 100%; - height: 100%; - } -} - .sidebar-left-wrapper, .sidebar-right-wrapper { transition: height 0.2s ease-in-out; diff --git a/app/src/styles/layout/skeleton.scss b/app/src/styles/layout/skeleton.scss index 9df0331..9dbc069 100644 --- a/app/src/styles/layout/skeleton.scss +++ b/app/src/styles/layout/skeleton.scss @@ -1,61 +1,90 @@ .skeleton-wrapper { - // max-width: 600px; - margin: 0 auto; - width: 100%; + margin: 0 auto; + width: 100%; - .skeleton { - background: var(--background-color-gray); + .skeleton { + background: var(--background-color-gray); - border-radius: 8px; - position: relative; - overflow: hidden; + border-radius: 8px; + position: relative; + overflow: hidden; - &::after { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-image: linear-gradient(90deg, - rgba(255, 255, 255, 0) 0%, - rgba(255, 255, 255, 0.2) 20%, - rgba(255, 255, 255, 0.5) 60%, - rgba(255, 255, 255, 0) 100%); - transform: translateX(-100%); - animation: shimmer 1.5s infinite; - } + &::after { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: linear-gradient( + 90deg, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.2) 20%, + rgba(255, 255, 255, 0.39) 60%, + rgba(255, 255, 255, 0) 100% + ); + transform: translateX(-100%); + animation: shimmer 1.5s infinite; + } + } + + .skeleton-header { + margin-bottom: 20px; + + .skeleton-title { + width: 100%; + height: 25px; + margin-bottom: 12px; } - .skeleton-header { - margin-bottom: 20px; - - .skeleton-title { - width: 100%; - height: 25px; - margin-bottom: 12px; - } - - .skeleton-subtitle { - width: 100%; - height: 4px; - } + .skeleton-subtitle { + width: 100%; + height: 4px; } + } - .skeleton-content { - display: flex; - flex-direction: column; - gap: 16px; + .skeleton-content { + display: flex; + flex-direction: column; + gap: 16px; - .skeleton-card { - width: 100%; - height: 15px; - } + .skeleton-card { + width: 100%; + height: 15px; } + } +} + +.asset-category-title{ + width: 60%; + height: 12px; + margin-bottom: 12px; + margin-top: 4px; +} +.skeleton-content-asset{ + display: flex; + height: calc(95px * 2 + 10px); + gap: 10px; + flex-wrap: wrap; + .skeleton-content { + gap: 8px; + flex-direction: column; + min-width: 122px; + min-height: 95px; + .asset-name { + width: 40%; + height: 10px; + } + .asset { + flex: 1; + width: 100%; + height: 100%; + } + } } @keyframes shimmer { - 100% { - transform: translateX(100%); - } -} \ No newline at end of file + 100% { + transform: translateX(100%); + } +} diff --git a/app/src/types/world/worldConstants.ts b/app/src/types/world/worldConstants.ts index 0374c8a..aaa8466 100644 --- a/app/src/types/world/worldConstants.ts +++ b/app/src/types/world/worldConstants.ts @@ -326,7 +326,7 @@ export const lineConfig: LineConfig = { }; export const wallConfig: WallConfig = { - defaultColor: "white", // Default color of the walls + defaultColor: "#f2f2f2", // Default color of the walls height: 7, // Height of the walls width: 0.05, // Width of the walls }; @@ -334,7 +334,7 @@ export const wallConfig: WallConfig = { export const floorConfig: FloorConfig = { defaultColor: "grey", // Default color of the floors height: 0.1, // Height of the floors - textureScale: 0.1, // Scale of the floor texture + textureScale: 1, // Scale of the floor texture }; export const roofConfig: RoofConfig = { @@ -345,7 +345,7 @@ export const roofConfig: RoofConfig = { export const aisleConfig: AisleConfig = { width: 0.1, // Width of the aisles height: 0.01, // Height of the aisles - defaultColor: 0xffff00, // Default color of the aisles + defaultColor: 0xE2AC09, // Default color of the aisles }; export const zoneConfig: ZoneConfig = { diff --git a/app/src/types/world/worldTypes.d.ts b/app/src/types/world/worldTypes.d.ts index c148038..3d6115a 100644 --- a/app/src/types/world/worldTypes.d.ts +++ b/app/src/types/world/worldTypes.d.ts @@ -225,7 +225,6 @@ interface AssetConfiguration { scale?: [number, number, number]; csgscale?: [number, number, number]; csgposition?: [number, number, number]; - positionY?: (intersectionPoint: { point: THREE.Vector3 }) => number; type?: "Fixed-Move" | "Free-Move"; } diff --git a/app/src/utils/shortcutkeys/handleShortcutKeys.ts b/app/src/utils/shortcutkeys/handleShortcutKeys.ts index 2ff5f39..c3346b8 100644 --- a/app/src/utils/shortcutkeys/handleShortcutKeys.ts +++ b/app/src/utils/shortcutkeys/handleShortcutKeys.ts @@ -17,7 +17,7 @@ import { useSelectedZoneStore } from "../../store/visualization/useZoneStore"; const KeyPressListener: React.FC = () => { const { activeModule, setActiveModule } = useModuleStore(); const { setActiveSubTool } = useActiveSubTool(); - const { toggleUI, setToggleUI } = useToggleStore(); + const { toggleUILeft, toggleUIRight, setToggleUI } = useToggleStore(); const { setToggleThreeD } = useThreeDStore(); const { setToolMode } = useToolMode(); const { setIsPlaying } = usePlayButtonStore(); @@ -26,7 +26,7 @@ const KeyPressListener: React.FC = () => { const { setAddAction } = useAddAction(); const { setSelectedWallItem } = useSelectedWallItem(); const { setActiveTool } = useActiveTool(); - const { clearSelectedZone} = useSelectedZoneStore(); + const { clearSelectedZone } = useSelectedZoneStore(); const isTextInput = (element: Element | null): boolean => element instanceof HTMLInputElement || @@ -42,9 +42,10 @@ const KeyPressListener: React.FC = () => { }; const module = modules[keyCombination]; if (module && !toggleView) { + console.log("hi"); setActiveTool("cursor"); setActiveSubTool("cursor"); - if (module === "market") setToggleUI(false); + if (module === "market") setToggleUI(false, false); setActiveModule(module); } }; @@ -69,6 +70,7 @@ const KeyPressListener: React.FC = () => { const toggleTo2D = toggleView; setToggleView(!toggleTo2D); setToggleThreeD(toggleTo2D); + setToggleUI(toggleTo2D, toggleTo2D); if (toggleTo2D) { setSelectedWallItem(null); setDeleteTool(false); @@ -105,6 +107,29 @@ const KeyPressListener: React.FC = () => { }; + const handleSidebarShortcuts = (key: string) => { + if (activeModule !== "market") { + if (key === "Ctrl+\\") { + if (toggleUILeft === toggleUIRight) { + setToggleUI(!toggleUILeft, !toggleUIRight); + } + else { + setToggleUI(true, true); + } + return; + } + if (key === "Ctrl+]") { + setToggleUI(toggleUILeft, !toggleUIRight); + return; + } + if (key === "Ctrl+[") { + setToggleUI(!toggleUILeft, toggleUIRight); + return; + } + } + } + + const handleKeyPress = (event: KeyboardEvent) => { if (isTextInput(document.activeElement)) return; @@ -113,11 +138,8 @@ const KeyPressListener: React.FC = () => { event.preventDefault(); - if (keyCombination === "Ctrl+\\") { - if (activeModule !== "market") setToggleUI(!toggleUI); - return; - } - + // Shortcuts specific for sidebar visibility toggle and others specific to sidebar if added + handleSidebarShortcuts(keyCombination); // Active module selection (builder, simulation, etc.) handleModuleSwitch(keyCombination); // Common editing tools: cursor | delete | free-hand @@ -132,6 +154,7 @@ const KeyPressListener: React.FC = () => { if (keyCombination === "ESCAPE") { setActiveTool("cursor"); + setActiveSubTool("cursor"); setIsPlaying(false); clearSelectedZone(); } @@ -146,7 +169,7 @@ const KeyPressListener: React.FC = () => { window.addEventListener("keydown", handleKeyPress); return () => window.removeEventListener("keydown", handleKeyPress); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [activeModule, toggleUI, toggleView]); + }, [activeModule, toggleUIRight, toggleUILeft, toggleView]); return null; }; diff --git a/app/src/utils/theme.ts b/app/src/utils/theme.ts index 9395129..541f4ba 100644 --- a/app/src/utils/theme.ts +++ b/app/src/utils/theme.ts @@ -4,7 +4,7 @@ export { }; function setTheme() { const savedTheme: string | null = localStorage.getItem('theme'); const systemPrefersDark: boolean = window.matchMedia('(prefers-color-scheme: dark)').matches; - const defaultTheme: string = savedTheme || (systemPrefersDark ? 'dark' : 'light'); + const defaultTheme: string = savedTheme ?? (systemPrefersDark ? 'dark' : 'light'); document.documentElement.setAttribute('data-theme', defaultTheme); localStorage.setItem('theme', defaultTheme); }