Refactor builder module and remove unused components

- Removed CalculateAreaGroup and computeArea function as they were no longer needed.
- Updated Floor2DInstance and Zone2DInstance to calculate area and centroid using new utility functions.
- Refactored WallInstances to include Floor2D rendering and improved area display.
- Cleaned up imports and ensured consistent formatting across files.
- Enhanced performance by memoizing calculations for area and centroid.
- Removed deprecated state management for rooms and version history visibility.
This commit is contained in:
2025-09-08 13:46:47 +05:30
parent f0d9cfe142
commit 23e7ba39e8
19 changed files with 562 additions and 645 deletions

View File

@@ -4,7 +4,7 @@ import { useLogger } from "../ui/log/LoggerContext";
import { GetLogIcon } from "./getLogIcons"; import { GetLogIcon } from "./getLogIcons";
import { CurserLeftIcon, CurserMiddleIcon, CurserRightIcon } from "../icons/LogIcons"; import { CurserLeftIcon, CurserMiddleIcon, CurserRightIcon } from "../icons/LogIcons";
import ShortcutHelper from "./shortcutHelper"; import ShortcutHelper from "./shortcutHelper";
import useVersionHistoryVisibleStore, { useShortcutStore } from "../../store/builder/store"; import { useShortcutStore } from "../../store/builder/store";
import { usePlayButtonStore } from "../../store/ui/usePlayButtonStore"; import { usePlayButtonStore } from "../../store/ui/usePlayButtonStore";
import useModuleStore, { useSubModuleStore } from "../../store/ui/useModuleStore"; import useModuleStore, { useSubModuleStore } from "../../store/ui/useModuleStore";
import { mouseActionHelper } from "../../utils/mouseUtils/mouseHelper"; import { mouseActionHelper } from "../../utils/mouseUtils/mouseHelper";
@@ -12,123 +12,118 @@ import { useMouseNoteStore } from "../../store/ui/useUIToggleStore";
import { useSceneContext } from "../../modules/scene/sceneContext"; import { useSceneContext } from "../../modules/scene/sceneContext";
const Footer: React.FC = () => { const Footer: React.FC = () => {
const { logs, setIsLogListVisible } = useLogger(); const { logs, setIsLogListVisible } = useLogger();
const lastLog = logs[logs.length - 1] || null; const lastLog = logs[logs.length - 1] || null;
const { setActiveModule } = useModuleStore(); const { setActiveModule } = useModuleStore();
const { setSubModule } = useSubModuleStore(); const { setSubModule } = useSubModuleStore();
const { setVersionHistoryVisible } = useVersionHistoryVisibleStore(); const { isPlaying } = usePlayButtonStore();
const { isPlaying } = usePlayButtonStore(); const { showShortcuts, setShowShortcuts } = useShortcutStore();
const { showShortcuts, setShowShortcuts } = useShortcutStore(); const { versionStore } = useSceneContext();
const { versionStore } = useSceneContext(); const { selectedVersion, setVersionHistoryVisible } = versionStore();
const { selectedVersion } = versionStore();
const { Leftnote, Middlenote, Rightnote } = useMouseNoteStore(); const { Leftnote, Middlenote, Rightnote } = useMouseNoteStore();
const [isOnline, setIsOnline] = useState<boolean>(navigator.onLine); const [isOnline, setIsOnline] = useState<boolean>(navigator.onLine);
// -------------------- Online/Offline Handlers -------------------- // -------------------- Online/Offline Handlers --------------------
const handleOnline = useCallback(() => { const handleOnline = useCallback(() => {
echo.success("You are back Online"); echo.success("You are back Online");
setIsOnline(true); setIsOnline(true);
}, []); }, []);
const handleOffline = useCallback(() => { const handleOffline = useCallback(() => {
echo.warn("Changes made now might not be saved"); echo.warn("Changes made now might not be saved");
echo.error("You are now Offline."); echo.error("You are now Offline.");
setIsOnline(false); setIsOnline(false);
}, []); }, []);
useEffect(() => { useEffect(() => {
window.addEventListener("online", handleOnline); window.addEventListener("online", handleOnline);
window.addEventListener("offline", handleOffline); window.addEventListener("offline", handleOffline);
return () => { return () => {
window.removeEventListener("online", handleOnline); window.removeEventListener("online", handleOnline);
window.removeEventListener("offline", handleOffline); window.removeEventListener("offline", handleOffline);
}; };
}, [handleOnline, handleOffline]); }, [handleOnline, handleOffline]);
// -------------------- Mouse Buttons -------------------- // -------------------- Mouse Buttons --------------------
const mouseButtons = useMemo( const mouseButtons = useMemo(
() => [ () => [
{ icon: <CurserLeftIcon />, label: Leftnote || "Pan", mouse: "left" }, { icon: <CurserLeftIcon />, label: Leftnote || "Pan", mouse: "left" },
{ icon: <CurserMiddleIcon />, label: Middlenote || "Scroll Zoom", mouse: "middle" }, { icon: <CurserMiddleIcon />, label: Middlenote || "Scroll Zoom", mouse: "middle" },
{ icon: <CurserRightIcon />, label: Rightnote || "Orbit / Cancel action", mouse: "right" }, { icon: <CurserRightIcon />, label: Rightnote || "Orbit / Cancel action", mouse: "right" },
], ],
[Leftnote, Middlenote, Rightnote] [Leftnote, Middlenote, Rightnote]
); );
// -------------------- Mouse Helper -------------------- // -------------------- Mouse Helper --------------------
useEffect(() => { useEffect(() => {
const cleanup = mouseActionHelper(); const cleanup = mouseActionHelper();
return () => cleanup(); return () => cleanup();
}, []); }, []);
return ( return (
<div className="footer-container"> <div className="footer-container">
<div className="footer-wrapper"> <div className="footer-wrapper">
{/* Mouse Button Info */} {/* Mouse Button Info */}
<div className="selection-wrapper"> <div className="selection-wrapper">
{mouseButtons.map(({ icon, label, mouse }) => ( {mouseButtons.map(({ icon, label, mouse }) => (
<div className="selector-wrapper" key={mouse}> <div className="selector-wrapper" key={mouse}>
<div className="icon">{icon}</div> <div className="icon">{icon}</div>
<div className="selector">{label}</div> <div className="selector">{label}</div>
</div>
))}
</div>
{/* Logs and Version */}
<div className="logs-wrapper">
<div className="bg-dummy left-top" />
<div className="bg-dummy right-bottom" />
<div className="log-container">
<button id="log-details-buttton" className={`logs-detail ${lastLog?.type ?? ""}`} onClick={() => setIsLogListVisible(true)}>
{lastLog ? (
<>
<span className="log-icon">{GetLogIcon(lastLog.type)}</span>
<span className="log-message">{lastLog.message}</span>
</>
) : (
"There are no logs to display at the moment."
)}
</button>
</div>
<div
className="version"
onClick={() => {
setVersionHistoryVisible(true);
setSubModule("properties");
setActiveModule("builder");
}}
>
{selectedVersion?.version ?? "v 0.0.0"}
<div className="icon">
<HelpIcon />
</div>
</div>
<div className={`wifi-connection ${isOnline ? "connected" : "disconnected"}`}>
<div className="icon">
<WifiIcon />
</div>
<div className="tooltip">{isOnline ? "Online" : "Offline"}</div>
</div>
</div>
</div> </div>
))}
{/* Shortcut Helper */}
{!isPlaying && showShortcuts && (
<div className="shortcut-helper-overlay visible">
<ShortcutHelper setShowShortcuts={setShowShortcuts} />
</div>
)}
</div> </div>
);
{/* Logs and Version */}
<div className="logs-wrapper">
<div className="bg-dummy left-top" />
<div className="bg-dummy right-bottom" />
<div className="log-container">
<button
id="log-details-buttton"
className={`logs-detail ${lastLog?.type ?? ""}`}
onClick={() => setIsLogListVisible(true)}
>
{lastLog ? (
<>
<span className="log-icon">{GetLogIcon(lastLog.type)}</span>
<span className="log-message">{lastLog.message}</span>
</>
) : (
"There are no logs to display at the moment."
)}
</button>
</div>
<div
className="version"
onClick={() => {
setVersionHistoryVisible(true);
setSubModule("properties");
setActiveModule("builder");
}}
>
{selectedVersion?.version ?? "v 0.0.0"}
<div className="icon">
<HelpIcon />
</div>
</div>
<div className={`wifi-connection ${isOnline ? "connected" : "disconnected"}`}>
<div className="icon">
<WifiIcon />
</div>
<div className="tooltip">{isOnline ? "Online" : "Offline"}</div>
</div>
</div>
</div>
{/* Shortcut Helper */}
{!isPlaying && showShortcuts && (
<div className="shortcut-helper-overlay visible">
<ShortcutHelper setShowShortcuts={setShowShortcuts} />
</div>
)}
</div>
);
}; };
export default Footer; export default Footer;

View File

@@ -1,13 +1,13 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import Header from "./Header"; import Header from "./Header";
import useModuleStore, { useSubModuleStore } from "../../../store/ui/useModuleStore"; import useModuleStore, { useSubModuleStore } from "../../../store/ui/useModuleStore";
import { AnalysisIcon, FilePackageIcon, MechanicsIcon, PropertiesIcon, SimulationIcon, } from "../../icons/SimulationIcons"; import { AnalysisIcon, FilePackageIcon, MechanicsIcon, PropertiesIcon, SimulationIcon } from "../../icons/SimulationIcons";
import { useToggleStore } from "../../../store/ui/useUIToggleStore"; import { useToggleStore } from "../../../store/ui/useUIToggleStore";
import Visualization from "./visualization/Visualization"; import Visualization from "./visualization/Visualization";
import Analysis from "./analysis/Analysis"; import Analysis from "./analysis/Analysis";
import Simulations from "./simulation/Simulations"; import Simulations from "./simulation/Simulations";
import useVersionHistoryVisibleStore, { useIsComparing, useToolMode } from "../../../store/builder/store"; import { useIsComparing, useToolMode } from "../../../store/builder/store";
import { useSelectedEventData, useSelectedEventSphere, } from "../../../store/simulation/useSimulationStore"; import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore";
import { useBuilderStore } from "../../../store/builder/useBuilderStore"; import { useBuilderStore } from "../../../store/builder/useBuilderStore";
import GlobalProperties from "./properties/GlobalProperties"; import GlobalProperties from "./properties/GlobalProperties";
import AssetProperties from "./properties/AssetProperties"; import AssetProperties from "./properties/AssetProperties";
@@ -22,6 +22,7 @@ import SelectedFloorProperties from "./properties/SelectedFloorProperties";
import SelectedDecalProperties from "./properties/SelectedDecalProperties"; import SelectedDecalProperties from "./properties/SelectedDecalProperties";
import SelectedAisleProperties from "./properties/SelectedAisleProperties"; import SelectedAisleProperties from "./properties/SelectedAisleProperties";
import ResourceManagement from "./resourceManagement/ResourceManagement"; import ResourceManagement from "./resourceManagement/ResourceManagement";
import { useSceneContext } from "../../../modules/scene/sceneContext";
type DisplayComponent = type DisplayComponent =
| "versionHistory" | "versionHistory"
@@ -51,7 +52,8 @@ const SideBarRight: React.FC = () => {
const { selectedWall, selectedFloor, selectedAisle, selectedFloorAsset } = useBuilderStore(); const { selectedWall, selectedFloor, selectedAisle, selectedFloorAsset } = useBuilderStore();
const { selectedEventData } = useSelectedEventData(); const { selectedEventData } = useSelectedEventData();
const { selectedEventSphere } = useSelectedEventSphere(); const { selectedEventSphere } = useSelectedEventSphere();
const { viewVersionHistory, setVersionHistoryVisible } = useVersionHistoryVisibleStore(); const { versionStore } = useSceneContext();
const { viewVersionHistory, setVersionHistoryVisible } = versionStore();
const { isComparing } = useIsComparing(); const { isComparing } = useIsComparing();
const [displayComponent, setDisplayComponent] = useState<DisplayComponent>("none"); const [displayComponent, setDisplayComponent] = useState<DisplayComponent>("none");
@@ -207,8 +209,7 @@ const SideBarRight: React.FC = () => {
<> <>
<button <button
id="sidebar-action-list-properties" id="sidebar-action-list-properties"
className={`sidebar-action-list ${subModule === "properties" ? "active" : "" className={`sidebar-action-list ${subModule === "properties" ? "active" : ""}`}
}`}
onClick={() => { onClick={() => {
setSubModule("properties"); setSubModule("properties");
setVersionHistoryVisible(false); setVersionHistoryVisible(false);
@@ -224,8 +225,7 @@ const SideBarRight: React.FC = () => {
<> <>
<button <button
id="sidebar-action-list-simulation" id="sidebar-action-list-simulation"
className={`sidebar-action-list ${subModule === "simulations" ? "active" : "" className={`sidebar-action-list ${subModule === "simulations" ? "active" : ""}`}
}`}
onClick={() => { onClick={() => {
setSubModule("simulations"); setSubModule("simulations");
setVersionHistoryVisible(false); setVersionHistoryVisible(false);
@@ -236,8 +236,7 @@ const SideBarRight: React.FC = () => {
</button> </button>
<button <button
id="sidebar-action-list-mechanics" id="sidebar-action-list-mechanics"
className={`sidebar-action-list ${subModule === "mechanics" ? "active" : "" className={`sidebar-action-list ${subModule === "mechanics" ? "active" : ""}`}
}`}
onClick={() => { onClick={() => {
setSubModule("mechanics"); setSubModule("mechanics");
setVersionHistoryVisible(false); setVersionHistoryVisible(false);
@@ -248,8 +247,7 @@ const SideBarRight: React.FC = () => {
</button> </button>
<button <button
id="sidebar-action-list-analysis" id="sidebar-action-list-analysis"
className={`sidebar-action-list ${subModule === "analysis" ? "active" : "" className={`sidebar-action-list ${subModule === "analysis" ? "active" : ""}`}
}`}
onClick={() => { onClick={() => {
setSubModule("analysis"); setSubModule("analysis");
setVersionHistoryVisible(false); setVersionHistoryVisible(false);
@@ -261,23 +259,19 @@ const SideBarRight: React.FC = () => {
</> </>
)} )}
{(activeModule === "builder" || {(activeModule === "builder" || activeModule === "simulation") && (
activeModule === "simulation") && ( <button
<button id="sidebar-action-list-properties"
id="sidebar-action-list-properties" className={`sidebar-action-list ${subModule === "resourceManagement" ? "active" : ""}`}
className={`sidebar-action-list ${subModule === "resourceManagement" ? "active" : "" onClick={() => {
}`} setSubModule("resourceManagement");
onClick={() => { setVersionHistoryVisible(false);
setSubModule("resourceManagement"); }}
setVersionHistoryVisible(false); >
}} <div className="tooltip">Resource Management</div>
> <FilePackageIcon isActive={subModule === "resourceManagement"} />
<div className="tooltip">Resource Management</div> </button>
<FilePackageIcon )}
isActive={subModule === "resourceManagement"}
/>
</button>
)}
</div> </div>
)} )}

View File

@@ -2,7 +2,6 @@ import { useParams } from "react-router-dom";
import { AddIcon, ArrowIcon, CloseIcon, KebabIcon, LocationIcon } from "../../../icons/ExportCommonIcons"; import { AddIcon, ArrowIcon, CloseIcon, KebabIcon, LocationIcon } from "../../../icons/ExportCommonIcons";
import { useSubModuleStore } from "../../../../store/ui/useModuleStore"; import { useSubModuleStore } from "../../../../store/ui/useModuleStore";
import { useSceneContext } from "../../../../modules/scene/sceneContext"; import { useSceneContext } from "../../../../modules/scene/sceneContext";
import useVersionHistoryVisibleStore from "../../../../store/builder/store";
import RenameInput from "../../../ui/inputs/RenameInput"; import RenameInput from "../../../ui/inputs/RenameInput";
import { getVersionDataApi } from "../../../../services/factoryBuilder/versionControl/getVersionDataApi"; import { getVersionDataApi } from "../../../../services/factoryBuilder/versionControl/getVersionDataApi";
@@ -10,8 +9,7 @@ import { getVersionDataApi } from "../../../../services/factoryBuilder/versionCo
const VersionHistory = () => { const VersionHistory = () => {
const { setSubModule } = useSubModuleStore(); const { setSubModule } = useSubModuleStore();
const { versionStore } = useSceneContext(); const { versionStore } = useSceneContext();
const { setVersionHistoryVisible } = useVersionHistoryVisibleStore(); const { versionHistory, setCreateNewVersion, selectedVersion, setSelectedVersion, setVersionHistoryVisible } = versionStore();
const { versionHistory, setCreateNewVersion, selectedVersion, setSelectedVersion } = versionStore();
const { projectId } = useParams(); const { projectId } = useParams();
const addNewVersion = () => { const addNewVersion = () => {
@@ -21,16 +19,16 @@ const VersionHistory = () => {
const handleSelectVersion = (version: Version) => { const handleSelectVersion = (version: Version) => {
if (!projectId) return; if (!projectId) return;
getVersionDataApi(projectId, version.versionId).then((versionData) => { getVersionDataApi(projectId, version.versionId)
setSelectedVersion(version); .then((versionData) => {
}).catch((err) => { setSelectedVersion(version);
echo.error(err); })
}) .catch((err) => {
echo.error(err);
});
}; };
const handleVersionNameChange = (newName: string, versionId: string) => { const handleVersionNameChange = (newName: string, versionId: string) => {};
};
return ( return (
<div className="version-history-container"> <div className="version-history-container">
@@ -38,11 +36,7 @@ const VersionHistory = () => {
<div className="version-history-header"> <div className="version-history-header">
<div className="version-history-title">Version History</div> <div className="version-history-title">Version History</div>
<div className="version-history-icons"> <div className="version-history-icons">
<button <button id="add-version" className="icon add-icon" onClick={addNewVersion}>
id="add-version"
className="icon add-icon"
onClick={addNewVersion}
>
<AddIcon /> <AddIcon />
</button> </button>
<div id="version-kebab" className="icon kebab-icon"> <div id="version-kebab" className="icon kebab-icon">
@@ -64,9 +58,7 @@ const VersionHistory = () => {
{/* Shortcut Info */} {/* Shortcut Info */}
<div className="version-history-shortcut-info"> <div className="version-history-shortcut-info">
<div className="info-icon">i</div> <div className="info-icon">i</div>
<div className="shortcut-text"> <div className="shortcut-text">Press Ctrl + Alt + S to add to version history while editing</div>
Press Ctrl + Alt + S to add to version history while editing
</div>
</div> </div>
{/* Current Version Display */} {/* Current Version Display */}
@@ -76,12 +68,8 @@ const VersionHistory = () => {
<LocationIcon /> <LocationIcon />
</div> </div>
<div className="location-details"> <div className="location-details">
<div className="current-version"> <div className="current-version">Current Version ({selectedVersion.version})</div>
Current Version ({selectedVersion.version}) <div className="saved-history-count">{versionHistory.length} Saved History</div>
</div>
<div className="saved-history-count">
{versionHistory.length} Saved History
</div>
</div> </div>
</div> </div>
)} )}
@@ -93,16 +81,8 @@ const VersionHistory = () => {
) : ( ) : (
versionHistory.map((version) => { versionHistory.map((version) => {
const key = `version-${version.versionId}`; const key = `version-${version.versionId}`;
return ( return <VersionHistoryItem key={key} version={version} onSelect={handleSelectVersion} onRename={handleVersionNameChange} />;
<VersionHistoryItem
key={key}
version={version}
onSelect={handleSelectVersion}
onRename={handleVersionNameChange}
/>
);
}) })
)} )}
</div> </div>
</div> </div>
@@ -119,22 +99,14 @@ type VersionHistoryItemProps = {
const VersionHistoryItem: React.FC<VersionHistoryItemProps> = ({ version, onSelect, onRename }) => { const VersionHistoryItem: React.FC<VersionHistoryItemProps> = ({ version, onSelect, onRename }) => {
return ( return (
<button <button className="saved-version">
className="saved-version" <div className="version-name" onClick={() => onSelect(version)}>
>
<div
className="version-name"
onClick={() => onSelect(version)}
>
v {version.version} v {version.version}
</div> </div>
<div className="version-details"> <div className="version-details">
<div className="details"> <div className="details">
<span className="timestamp"> <span className="timestamp">
<RenameInput <RenameInput value={version.versionName ? version.versionName : version.timeStamp} onRename={(newName) => onRename(newName, version.versionId)} />
value={version.versionName ? version.versionName : version.timeStamp}
onRename={(newName) => onRename(newName, version.versionId)}
/>
</span> </span>
<span className="saved-by"> <span className="saved-by">
<div className="user-profile">{version.createdBy[0]}</div> <div className="user-profile">{version.createdBy[0]}</div>

View File

@@ -1,104 +1,84 @@
import React from "react"; import React from "react";
import useModuleStore from "../../store/ui/useModuleStore"; import useModuleStore from "../../store/ui/useModuleStore";
import { import { BuilderIcon, CartIcon, SimulationIcon, VisualizationIcon } from "../icons/ExportModuleIcons";
BuilderIcon, import { useToggleStore } from "../../store/ui/useUIToggleStore";
CartIcon, import { useSceneContext } from "../../modules/scene/sceneContext";
SimulationIcon,
VisualizationIcon,
} from "../icons/ExportModuleIcons";
import {useToggleStore} from "../../store/ui/useUIToggleStore";
import useVersionStore from "../../store/builder/store";
const ModuleToggle: React.FC = () => { const ModuleToggle: React.FC = () => {
const { activeModule, setActiveModule } = useModuleStore(); const { activeModule, setActiveModule } = useModuleStore();
const { setToggleUI } = useToggleStore(); const { setToggleUI } = useToggleStore();
const { setVersionHistoryVisible } = useVersionStore(); const { versionStore } = useSceneContext();
const { setVersionHistoryVisible } = versionStore();
return ( return (
<div className="module-toggle-container"> <div className="module-toggle-container">
<button <button
id={"builder"} id={"builder"}
className={`module-list ${activeModule === "builder" ? "active" : ""}`} className={`module-list ${activeModule === "builder" ? "active" : ""}`}
onClick={() => { onClick={() => {
setActiveModule("builder"); setActiveModule("builder");
setVersionHistoryVisible(false); setVersionHistoryVisible(false);
setToggleUI( setToggleUI(
localStorage.getItem("navBarUiLeft") localStorage.getItem("navBarUiLeft") ? localStorage.getItem("navBarUiLeft") === "true" : true,
? localStorage.getItem("navBarUiLeft") === "true" localStorage.getItem("navBarUiRight") ? localStorage.getItem("navBarUiRight") === "true" : true
: true, );
localStorage.getItem("navBarUiRight") }}
? localStorage.getItem("navBarUiRight") === "true" >
: true <div className="icon">
); <BuilderIcon isActive={activeModule === "builder"} />
}} </div>
> <div className="module">Builder</div>
<div className="icon"> </button>
<BuilderIcon isActive={activeModule === "builder"} /> <button
id={"simulation"}
className={`module-list ${activeModule === "simulation" ? "active" : ""}`}
onClick={() => {
setActiveModule("simulation");
setVersionHistoryVisible(false);
setToggleUI(
localStorage.getItem("navBarUiLeft") ? localStorage.getItem("navBarUiLeft") === "true" : true,
localStorage.getItem("navBarUiRight") ? localStorage.getItem("navBarUiRight") === "true" : true
);
}}
>
<div className="icon">
<SimulationIcon isActive={activeModule === "simulation"} />
</div>
<div className="module">Simulation</div>
</button>
<button
id={"visualization"}
className={`module-list ${activeModule === "visualization" ? "active" : ""}`}
onClick={() => {
setActiveModule("visualization");
setVersionHistoryVisible(false);
setToggleUI(
localStorage.getItem("navBarUiLeft") ? localStorage.getItem("navBarUiLeft") === "true" : true,
localStorage.getItem("navBarUiRight") ? localStorage.getItem("navBarUiRight") === "true" : true
);
}}
>
<div className="icon">
<VisualizationIcon isActive={activeModule === "visualization"} />
</div>
<div className="module">Visualization</div>
</button>
<button
id={"market"}
className={`module-list ${activeModule === "market" ? "active" : ""}`}
onClick={() => {
setActiveModule("market");
setVersionHistoryVisible(false);
setToggleUI(false, false);
}}
>
<div className="icon">
<CartIcon isActive={activeModule === "market"} />
</div>
<div className="module">Market Place</div>
</button>
</div> </div>
<div className="module">Builder</div> );
</button>
<button
id={"simulation"}
className={`module-list ${
activeModule === "simulation" ? "active" : ""
}`}
onClick={() => {
setActiveModule("simulation");
setVersionHistoryVisible(false);
setToggleUI(
localStorage.getItem("navBarUiLeft")
? localStorage.getItem("navBarUiLeft") === "true"
: true,
localStorage.getItem("navBarUiRight")
? localStorage.getItem("navBarUiRight") === "true"
: true
);
}}
>
<div className="icon">
<SimulationIcon isActive={activeModule === "simulation"} />
</div>
<div className="module">Simulation</div>
</button>
<button
id={"visualization"}
className={`module-list ${
activeModule === "visualization" ? "active" : ""
}`}
onClick={() => {
setActiveModule("visualization");
setVersionHistoryVisible(false);
setToggleUI(
localStorage.getItem("navBarUiLeft")
? localStorage.getItem("navBarUiLeft") === "true"
: true,
localStorage.getItem("navBarUiRight")
? localStorage.getItem("navBarUiRight") === "true"
: true
);
}}
>
<div className="icon">
<VisualizationIcon isActive={activeModule === "visualization"} />
</div>
<div className="module">Visualization</div>
</button>
<button
id={"market"}
className={`module-list ${activeModule === "market" ? "active" : ""}`}
onClick={() => {
setActiveModule("market");
setVersionHistoryVisible(false);
setToggleUI(false, false);
}}
>
<div className="icon">
<CartIcon isActive={activeModule === "market"} />
</div>
<div className="module">Market Place</div>
</button>
</div>
);
}; };
export default ModuleToggle; export default ModuleToggle;

View File

@@ -2,7 +2,7 @@ import React, { useState } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { ArrowIcon } from "../../icons/ExportCommonIcons"; import { ArrowIcon } from "../../icons/ExportCommonIcons";
import { toggleTheme } from "../../../utils/theme"; import { toggleTheme } from "../../../utils/theme";
import useVersionHistoryVisibleStore, { useShortcutStore } from "../../../store/builder/store"; import { useShortcutStore } from "../../../store/builder/store";
import useModuleStore, { useSubModuleStore } from "../../../store/ui/useModuleStore"; import useModuleStore, { useSubModuleStore } from "../../../store/ui/useModuleStore";
import { useSceneContext } from "../../../modules/scene/sceneContext"; import { useSceneContext } from "../../../modules/scene/sceneContext";
@@ -25,8 +25,7 @@ const MenuBar: React.FC<MenuBarProps> = ({ setOpenMenu }) => {
const [selectedItems, setSelectedItems] = useState<Record<string, boolean>>({}); const [selectedItems, setSelectedItems] = useState<Record<string, boolean>>({});
const { versionStore } = useSceneContext(); const { versionStore } = useSceneContext();
const { setCreateNewVersion } = versionStore(); const { setCreateNewVersion, setVersionHistoryVisible } = versionStore();
const { setVersionHistoryVisible } = useVersionHistoryVisibleStore();
const { setActiveModule } = useModuleStore(); const { setActiveModule } = useModuleStore();
const { setSubModule } = useSubModuleStore(); const { setSubModule } = useSubModuleStore();
const { showShortcuts, setShowShortcuts } = useShortcutStore(); const { showShortcuts, setShowShortcuts } = useShortcutStore();
@@ -79,7 +78,16 @@ const MenuBar: React.FC<MenuBarProps> = ({ setOpenMenu }) => {
{ label: "Import" }, { label: "Import" },
{ label: "Close File" }, { label: "Close File" },
], ],
Edit: [{ label: "Undo", shortcut: "Ctrl + Z" }, { label: "Redo", shortcut: "Ctrl + Shift + Z" }, { label: "Undo History" }, { label: "Redo History" }, { label: "Find", shortcut: "Ctrl + F" }, { label: "Delete" }, { label: "Select by..." }, { label: "Keymap" }], Edit: [
{ label: "Undo", shortcut: "Ctrl + Z" },
{ label: "Redo", shortcut: "Ctrl + Shift + Z" },
{ label: "Undo History" },
{ label: "Redo History" },
{ label: "Find", shortcut: "Ctrl + F" },
{ label: "Delete" },
{ label: "Select by..." },
{ label: "Keymap" },
],
View: [ View: [
{ label: "Grid" }, { label: "Grid" },
{ {
@@ -172,7 +180,13 @@ const MenuBar: React.FC<MenuBarProps> = ({ setOpenMenu }) => {
<div className="dropdown-menu"> <div className="dropdown-menu">
{items.map((item) => {items.map((item) =>
item.submenu ? ( item.submenu ? (
<button id={item.label} key={item.label} className="menu-item-container" onMouseEnter={() => setActiveSubMenu(item.label)} onMouseLeave={() => setActiveSubMenu(null)}> <button
id={item.label}
key={item.label}
className="menu-item-container"
onMouseEnter={() => setActiveSubMenu(item.label)}
onMouseLeave={() => setActiveSubMenu(null)}
>
<div className="menu-item"> <div className="menu-item">
<span>{item.label}</span> <span>{item.label}</span>
<span className="dropdown-icon"> <span className="dropdown-icon">

View File

@@ -17,7 +17,6 @@ import SocketResponses from "../collaboration/socket/socketResponses.dev";
import Ground from "../scene/environment/ground"; import Ground from "../scene/environment/ground";
import MeasurementTool from "../scene/tools/measurementTool"; import MeasurementTool from "../scene/tools/measurementTool";
import NavMesh from "../simulation/vehicle/navMesh/navMesh"; import NavMesh from "../simulation/vehicle/navMesh/navMesh";
import CalculateAreaGroup from "./groups/calculateAreaGroup";
import LayoutImage from "./layout/layoutImage"; import LayoutImage from "./layout/layoutImage";
import AssetsGroup from "./asset/assetsGroup"; import AssetsGroup from "./asset/assetsGroup";
import DxfFile from "./dfx/LoadBlueprint"; import DxfFile from "./dfx/LoadBlueprint";
@@ -51,8 +50,8 @@ export default function Builder() {
if (!toggleView) { if (!toggleView) {
setHoveredLine(null); setHoveredLine(null);
setHoveredPoint(null); setHoveredPoint(null);
state.gl.domElement.style.cursor = 'default'; state.gl.domElement.style.cursor = "default";
setToolMode('cursor'); setToolMode("cursor");
} }
}, [toggleView]); }, [toggleView]);
@@ -65,14 +64,14 @@ export default function Builder() {
setShadows(data.shadowVisibility); setShadows(data.shadowVisibility);
setRenderDistance(data.renderDistance); setRenderDistance(data.renderDistance);
setLimitDistance(data.limitDistance); setLimitDistance(data.limitDistance);
}) });
}, [projectId]); }, [projectId]);
useFrame(() => { useFrame(() => {
if (csgRef.current && selectedWallAsset) { if (csgRef.current && selectedWallAsset) {
csgRef.current.update(); csgRef.current.update();
} }
}) });
return ( return (
<> <>
@@ -82,13 +81,11 @@ export default function Builder() {
<AssetsGroup plane={plane} /> <AssetsGroup plane={plane} />
<mesh name='Walls-And-WallAssets-Group'> <mesh name="Walls-And-WallAssets-Group">
<Geometry ref={csgRef} useGroups> <Geometry ref={csgRef} useGroups>
<WallGroup /> <WallGroup />
<WallAssetGroup /> <WallAssetGroup />
</Geometry> </Geometry>
</mesh> </mesh>
@@ -102,8 +99,6 @@ export default function Builder() {
<MeasurementTool /> <MeasurementTool />
<CalculateAreaGroup />
<NavMesh /> <NavMesh />
<DxfFile /> <DxfFile />

View File

@@ -1,50 +1,61 @@
import { useMemo } from 'react'; import { useMemo } from "react";
import { DoubleSide, Shape, Vector2 } from 'three'; import { DoubleSide, Shape, Vector2 } from "three";
import { Extrude } from '@react-three/drei'; import { Extrude, Html } from "@react-three/drei";
import * as Constants from '../../../../../types/world/worldConstants'; import * as Constants from "../../../../../types/world/worldConstants";
import getCenteroidPoint from "../../../functions/getCenteroid";
import getArea from "../../../functions/getArea";
function Floor2DInstance({ floor }: { readonly floor: Floor }) { function Floor2DInstance({ floor }: { readonly floor: Floor }) {
const savedTheme: string | null = localStorage.getItem("theme"); const savedTheme: string | null = localStorage.getItem("theme");
const points2D = useMemo(() => {
return floor.points.map((p) => new Vector2(parseFloat(p.position[0].toFixed(2)), parseFloat(p.position[2].toFixed(2))));
}, [floor]);
const shape = useMemo(() => { const shape = useMemo(() => {
const shape = new Shape(); const shape = new Shape();
const points = floor.points.map(p => new Vector2(p.position[0], p.position[2])); shape.moveTo(points2D[0].x, points2D[0].y);
if (points.length < 3) return null; for (let i = 1; i < points2D.length; i++) {
shape.moveTo(points[0].x, points[0].y); shape.lineTo(points2D[i].x, points2D[i].y);
for (let i = 1; i < points.length; i++) {
shape.lineTo(points[i].x, points[i].y);
} }
shape.lineTo(points2D[0].x, points2D[0].y);
return shape; return shape;
}, [floor]); }, [points2D]);
const area = useMemo(() => getArea(points2D), [points2D]);
const centroid: [number, number, number] = useMemo(() => {
const center = getCenteroidPoint(points2D);
if (!center) return [0, Constants.floorConfig.height + 0.01, 0];
return [center.x, Constants.floorConfig.height + 0.01, center.y] as [number, number, number];
}, [points2D]);
if (!shape) return null; if (!shape) return null;
const formattedArea = `${area.toFixed(2)}`;
return ( return (
<mesh <>
castShadow <mesh castShadow receiveShadow name={`Floor-2D-${floor.floorUuid}`} rotation={[Math.PI / 2, 0, 0]} position={[0, 0, 0]} userData={floor}>
receiveShadow <Extrude name={`Floor-${floor.floorUuid}`} args={[shape, { depth: Constants.floorConfig.height }]} userData={floor}>
name={`Floor-2D-${floor.floorUuid}`} <meshBasicMaterial
rotation={[Math.PI / 2, 0, 0]} color={savedTheme === "dark" ? Constants.lineConfig.floorColor : Constants.lineConfig.floorColor}
position={[0, 0, 0]} side={DoubleSide}
userData={floor} transparent
> opacity={0.4}
<Extrude depthWrite={false}
name={`Floor-${floor.floorUuid}`} />
args={[shape, { </Extrude>
depth: Constants.floorConfig.height, </mesh>
}]}
userData={floor} <Html key={floor.floorUuid} position={centroid} wrapperClass="distance-text-wrapper" className="distance-text" zIndexRange={[1, 0]} prepend center sprite>
> <div className="distance area">
<meshBasicMaterial {floor.floorName} ({formattedArea})
color={savedTheme === "dark" ? "#808080" : "#808080"} </div>
side={DoubleSide} </Html>
transparent </>
opacity={0.4}
depthWrite={false}
/>
</Extrude>
</mesh>
); );
} }
export default Floor2DInstance; export default Floor2DInstance;

View File

@@ -1,14 +1,14 @@
import React, { useEffect, useMemo } from 'react'; import React, { useEffect, useMemo } from "react";
import { Vector3 } from 'three'; import { Vector3 } from "three";
import { Html } from '@react-three/drei'; import { Html } from "@react-three/drei";
import { useSceneContext } from '../../../scene/sceneContext'; import { useSceneContext } from "../../../scene/sceneContext";
import { useToggleView, useToolMode } from '../../../../store/builder/store'; import { useToggleView, useToolMode } from "../../../../store/builder/store";
import Line from '../../line/line'; import Line from "../../line/line";
import Point from '../../point/point'; import Point from "../../point/point";
import FloorInstance from './Instance/floorInstance'; import FloorInstance from "./Instance/floorInstance";
import Floor2DInstance from './Instance/floor2DInstance'; import Floor2DInstance from "./Instance/floor2DInstance";
import useModuleStore from '../../../../store/ui/useModuleStore'; import useModuleStore from "../../../../store/ui/useModuleStore";
import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
function FloorInstances() { function FloorInstances() {
const { floorStore } = useSceneContext(); const { floorStore } = useSceneContext();
@@ -20,11 +20,11 @@ function FloorInstances() {
useEffect(() => { useEffect(() => {
// console.log('floors: ', floors); // console.log('floors: ', floors);
}, [floors]) }, [floors]);
useEffect(() => { useEffect(() => {
if (!toggleView && activeModule === 'builder') { if (!toggleView && activeModule === "builder") {
if (toolMode !== 'cursor') { if (toolMode !== "cursor") {
if (selectedFloor) setSelectedFloor(null); if (selectedFloor) setSelectedFloor(null);
} }
} else if (selectedFloor) { } else if (selectedFloor) {
@@ -36,8 +36,8 @@ function FloorInstances() {
const points: Point[] = []; const points: Point[] = [];
const seenUuids = new Set<string>(); const seenUuids = new Set<string>();
floors.forEach(floor => { floors.forEach((floor) => {
floor.points.forEach(point => { floor.points.forEach((point) => {
if (!seenUuids.has(point.pointUuid)) { if (!seenUuids.has(point.pointUuid)) {
seenUuids.add(point.pointUuid); seenUuids.add(point.pointUuid);
points.push(point); points.push(point);
@@ -65,7 +65,7 @@ function FloorInstances() {
lines.push({ lines.push({
start: current, start: current,
end: next, end: next,
key: lineKey key: lineKey,
}); });
} }
} }
@@ -76,9 +76,8 @@ function FloorInstances() {
return ( return (
<> <>
{!toggleView && floors.length > 0 && ( {!toggleView && floors.length > 0 && (
<mesh name='Floors-Group'> <mesh name="Floors-Group">
{floors.map((floor) => ( {floors.map((floor) => (
<FloorInstance key={floor.floorUuid} floor={floor} /> <FloorInstance key={floor.floorUuid} floor={floor} />
))} ))}
@@ -86,7 +85,7 @@ function FloorInstances() {
)} )}
{toggleView && floors.length > 0 && ( {toggleView && floors.length > 0 && (
<mesh name='Floors-2D-Group'> <mesh name="Floors-2D-Group">
{floors.map((floor) => ( {floors.map((floor) => (
<Floor2DInstance key={floor.floorUuid} floor={floor} /> <Floor2DInstance key={floor.floorUuid} floor={floor} />
))} ))}
@@ -95,14 +94,13 @@ function FloorInstances() {
{toggleView && ( {toggleView && (
<> <>
<group name='Floor-Points-Group'> <group name="Floor-Points-Group">
{allPoints.map((point) => ( {allPoints.map((point) => (
<Point key={point.pointUuid} point={point} /> <Point key={point.pointUuid} point={point} />
))} ))}
</group> </group>
<group name='Floor-Lines-Group'> <group name="Floor-Lines-Group">
{allLines.map(({ start, end, key }) => ( {allLines.map(({ start, end, key }) => (
<Line key={key} points={[start, end]} /> <Line key={key} points={[start, end]} />
))} ))}
@@ -114,7 +112,7 @@ function FloorInstances() {
return ( return (
<React.Fragment key={key}> <React.Fragment key={key}>
{toggleView && {toggleView && (
<Html <Html
key={`${start.pointUuid}_${end.pointUuid}`} key={`${start.pointUuid}_${end.pointUuid}`}
userData={line} userData={line}
@@ -125,24 +123,19 @@ function FloorInstances() {
prepend prepend
sprite sprite
> >
<div <div key={key} className={`distance ${key}`}>
key={key}
className={`distance ${key}`}
>
{distance.toFixed(2)} m {distance.toFixed(2)} m
</div> </div>
</Html> </Html>
} )}
</React.Fragment> </React.Fragment>
) );
})} })}
</group> </group>
</> </>
)} )}
</> </>
) );
} }
export default FloorInstances; export default FloorInstances;

View File

@@ -1,29 +0,0 @@
import { Vector2 } from "three";
export function computeArea(data: any, type: "zone" | "rooms" | "aisle"): any {
if (type === "zone") {
const points3D = data.map((p: any) => new Vector2(p[0], p[2]));
let area = 0;
for (let i = 0; i < points3D.length - 1; i++) {
const current = points3D[i];
const next = points3D[i + 1];
area += current.x * next.y - next.x * current.y;
}
return Math.abs(area) / 2;
}
if (type === "rooms") {
const points2D = data.coordinates.map(
(coordinate: any) =>
new Vector2(coordinate.position.x, coordinate.position.z)
);
let area = 0;
for (let i = 0; i < points2D.length - 1; i++) {
const current = points2D[i];
const next = points2D[i + 1];
area += current.x * next.y - next.x * current.y;
}
return Math.abs(area) / 2;
}
}

View File

@@ -0,0 +1,10 @@
import { Vector2 } from "three";
export default function getArea(points: Vector2[]): number {
let sum = 0;
for (let i = 0; i < points.length; i++) {
const j = (i + 1) % points.length;
sum += points[i].x * points[j].y - points[j].x * points[i].y;
}
return Math.abs(sum / 2);
}

View File

@@ -0,0 +1,48 @@
import { Vector2 } from "three";
export default function getCenteroidPoint(points: Vector2[]): Vector2 | null {
if (points.length < 3) return null;
let minZ = points[0].y;
let maxZ = points[0].y;
for (let i = 1; i < points.length; i++) {
minZ = Math.min(minZ, points[i].y);
maxZ = Math.max(maxZ, points[i].y);
}
const scanZ = minZ + (maxZ - minZ) * 0.51;
const intersections: number[] = [];
for (let i = 0; i < points.length; i++) {
const p1 = points[i];
const p2 = points[(i + 1) % points.length];
if (p1.y === p2.y) continue;
const [top, bottom] = p1.y > p2.y ? [p1, p2] : [p2, p1];
if (scanZ >= bottom.y && scanZ < top.y) {
const t = (scanZ - bottom.y) / (top.y - bottom.y);
const x = bottom.x + t * (top.x - bottom.x);
intersections.push(x);
}
}
intersections.sort((a, b) => a - b);
if (intersections.length >= 2) {
const x = (intersections[0] + intersections[1]) / 2;
return new Vector2(x, scanZ);
}
let x = 0,
z = 0;
for (const p of points) {
x += p.x;
z += p.y;
}
x /= points.length;
z /= points.length;
return new Vector2(x, z);
}

View File

@@ -1,106 +0,0 @@
import { useRoomsState, useToggleView } from "../../../store/builder/store";
import { computeArea } from "../functions/computeArea";
import { Html } from "@react-three/drei";
import * as CONSTANTS from "../../../types/world/worldConstants";
import * as turf from "@turf/turf";
import * as THREE from "three";
const CalculateAreaGroup = () => {
const { roomsState } = useRoomsState();
const { toggleView } = useToggleView();
const savedTheme: string | null = localStorage.getItem("theme");
return (
<group name="roomArea" visible={toggleView}>
<group name="roomFills" visible={toggleView}>
{roomsState.length > 0 &&
roomsState.flat().map((room: any, index: number) => {
const coordinates = room.coordinates;
if (!coordinates || coordinates.length < 3) return null;
const coords2D = coordinates.map((p: any) => new THREE.Vector2(p.position.x, p.position.z));
if (!coords2D[0].equals(coords2D[coords2D.length - 1])) {
coords2D.push(coords2D[0]);
}
const shape = new THREE.Shape(coords2D);
const extrudeSettings = {
depth: 0.01,
bevelEnabled: false,
};
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
geometry.rotateX(Math.PI / 2);
const material = new THREE.MeshBasicMaterial({
color: savedTheme === "dark" ? "#d2baff" : "#6f42c1",
side: THREE.DoubleSide,
transparent: true,
opacity: 0.4,
depthWrite: false,
});
return (
<group key={`roomFill-${index}`}>
<mesh
geometry={geometry}
material={material}
position={[0, 0.12, 0]}
/>
</group>
);
})}
</group>
{roomsState.length > 0 &&
roomsState.flat().map((room: any, index: number) => {
if (!toggleView) return null;
const coordinates = room.coordinates;
if (!coordinates || coordinates.length < 3) return null;
let coords2D = coordinates.map((p: any) => [p.position.x, p.position.z,]);
const first = coords2D[0];
const last = coords2D[coords2D.length - 1];
if (first[0] !== last[0] || first[1] !== last[1]) {
coords2D.push(first);
}
const polygon = turf.polygon([coords2D]);
const center2D = turf.center(polygon).geometry.coordinates;
const sumY = coordinates.reduce((sum: number, p: any) => sum + p.position.y, 0);
const avgY = sumY / coordinates.length;
const area = computeArea(room, "rooms");
const formattedArea = `${area.toFixed(2)}`;
const htmlPosition: [number, number, number] = [
center2D[0],
avgY + CONSTANTS.zoneConfig.height,
center2D[1],
];
return (
<Html
key={`${index}-${room.layer || index}`}
position={htmlPosition}
wrapperClass="distance-text-wrapper"
className="distance-text"
zIndexRange={[1, 0]}
prepend
center
sprite
>
<div className={`distance area line-${room.layer || index}`}>
Room ({formattedArea})
</div>
</Html>
);
})}
</group>
);
};
export default CalculateAreaGroup;

View File

@@ -1,19 +1,21 @@
import React, { useEffect, useMemo } from 'react'; import React, { useEffect, useMemo } from "react";
import { DoubleSide, RepeatWrapping, Shape, SRGBColorSpace, TextureLoader, Vector2, Vector3 } from 'three'; import { DoubleSide, RepeatWrapping, Shape, SRGBColorSpace, TextureLoader, Vector2, Vector3 } from "three";
import { Html, Extrude } from '@react-three/drei'; import { Html, Extrude } from "@react-three/drei";
import { useLoader } from '@react-three/fiber'; import { useLoader } from "@react-three/fiber";
import { useBuilderStore } from '../../../../store/builder/useBuilderStore'; import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
import { useSceneContext } from '../../../scene/sceneContext'; import { useSceneContext } from "../../../scene/sceneContext";
import { useToggleView, useToolMode } from '../../../../store/builder/store'; import { useToggleView, useToolMode } from "../../../../store/builder/store";
import { useWallClassification } from './instance/helpers/useWallClassification'; import { useWallClassification } from "./instance/helpers/useWallClassification";
import Line from '../../line/line'; import Line from "../../line/line";
import Point from '../../point/point'; import Point from "../../point/point";
import WallInstance from './instance/wallInstance'; import WallInstance from "./instance/wallInstance";
import * as Constants from '../../../../types/world/worldConstants'; import * as Constants from "../../../../types/world/worldConstants";
import texturePath from "../../../../assets/textures/floor/white.png"; import texturePath from "../../../../assets/textures/floor/white.png";
import texturePathDark from "../../../../assets/textures/floor/black.png"; import texturePathDark from "../../../../assets/textures/floor/black.png";
import useModuleStore from '../../../../store/ui/useModuleStore'; import useModuleStore from "../../../../store/ui/useModuleStore";
import getCenteroidPoint from "../../functions/getCenteroid";
import getArea from "../../functions/getArea";
function WallInstances() { function WallInstances() {
const { wallStore } = useSceneContext(); const { wallStore } = useSceneContext();
@@ -26,11 +28,11 @@ function WallInstances() {
useEffect(() => { useEffect(() => {
// console.log('walls: ', walls); // console.log('walls: ', walls);
}, [walls]) }, [walls]);
useEffect(() => { useEffect(() => {
if (!toggleView && activeModule === 'builder') { if (!toggleView && activeModule === "builder") {
if (toolMode !== 'cursor') { if (toolMode !== "cursor") {
if (selectedWall) setSelectedWall(null); if (selectedWall) setSelectedWall(null);
} }
} else { } else {
@@ -42,8 +44,8 @@ function WallInstances() {
const points: Point[] = []; const points: Point[] = [];
const seenUuids = new Set<string>(); const seenUuids = new Set<string>();
walls.forEach(wall => { walls.forEach((wall) => {
wall.points.forEach(point => { wall.points.forEach((point) => {
if (!seenUuids.has(point.pointUuid)) { if (!seenUuids.has(point.pointUuid)) {
seenUuids.add(point.pointUuid); seenUuids.add(point.pointUuid);
points.push(point); points.push(point);
@@ -62,9 +64,9 @@ function WallInstances() {
<WallInstance key={wall.wallUuid} wall={wall} /> <WallInstance key={wall.wallUuid} wall={wall} />
))} ))}
<group name='Wall-Floors-Group'> <group name="Wall-Floors-Group">
{rooms.map((room, index) => ( {rooms.map((room, index) => (
<Floor key={index} room={room} /> <Floor3D key={index} room={room} />
))} ))}
</group> </group>
</> </>
@@ -72,14 +74,19 @@ function WallInstances() {
{toggleView && ( {toggleView && (
<> <>
<group name='Wall-Points-Group'> <group name="Wall-Points-Group">
{allPoints.map((point) => ( {allPoints.map((point) => (
<Point key={point.pointUuid} point={point} /> <Point key={point.pointUuid} point={point} />
))} ))}
</group> </group>
<group name='Wall-Lines-Group'> <group name="Wall-Floors-Group">
{rooms.map((room, index) => (
<Floor2D key={index} room={room} />
))}
</group>
<group name="Wall-Lines-Group">
{walls.map((wall) => ( {walls.map((wall) => (
<React.Fragment key={wall.wallUuid}> <React.Fragment key={wall.wallUuid}>
<Line points={wall.points} /> <Line points={wall.points} />
@@ -91,8 +98,8 @@ function WallInstances() {
const distance = new Vector3(...wall.points[0].position).distanceTo(new Vector3(...wall.points[1].position)); const distance = new Vector3(...wall.points[0].position).distanceTo(new Vector3(...wall.points[1].position));
return ( return (
< React.Fragment key={wall.wallUuid}> <React.Fragment key={wall.wallUuid}>
{toggleView && {toggleView && (
<Html <Html
key={`${wall.points[0].pointUuid}_${wall.points[1].pointUuid}`} key={`${wall.points[0].pointUuid}_${wall.points[1].pointUuid}`}
userData={wall} userData={wall}
@@ -103,29 +110,25 @@ function WallInstances() {
prepend prepend
sprite sprite
> >
<div <div key={wall.wallUuid} className={`distance ${wall.wallUuid}`}>
key={wall.wallUuid}
className={`distance ${wall.wallUuid}`}
>
{distance.toFixed(2)} m {distance.toFixed(2)} m
</div> </div>
</Html> </Html>
} )}
</React.Fragment> </React.Fragment>
) );
})} })}
</group> </group>
</> </>
)} )}
</> </>
) );
} }
export default WallInstances; export default WallInstances;
function Floor({ room }: { readonly room: Point[] }) { function Floor3D({ room }: { readonly room: Point[] }) {
const savedTheme: string | null = localStorage.getItem('theme'); const savedTheme: string | null = localStorage.getItem("theme");
const textureScale = Constants.floorConfig.textureScale; const textureScale = Constants.floorConfig.textureScale;
const floorTexture = useLoader(TextureLoader, savedTheme === "dark" ? texturePathDark : texturePath); const floorTexture = useLoader(TextureLoader, savedTheme === "dark" ? texturePathDark : texturePath);
floorTexture.wrapS = floorTexture.wrapT = RepeatWrapping; floorTexture.wrapS = floorTexture.wrapT = RepeatWrapping;
@@ -134,10 +137,12 @@ function Floor({ room }: { readonly room: Point[] }) {
const shape = useMemo(() => { const shape = useMemo(() => {
const shape = new Shape(); const shape = new Shape();
const points = room.map(p => new Vector2(p.position[0], p.position[2])); const points = room.map((p) => new Vector2(p.position[0], p.position[2]));
if (points.length < 3) return null; if (points.length < 3) return null;
shape.moveTo(points[0].x, points[0].y); shape.moveTo(points[0].x, points[0].y);
points.forEach((pt) => { shape.lineTo(pt.x, pt.y); }); points.forEach((pt) => {
shape.lineTo(pt.x, pt.y);
});
return shape; return shape;
}, [room]); }, [room]);
@@ -145,15 +150,54 @@ function Floor({ room }: { readonly room: Point[] }) {
return ( return (
<mesh name="Wall-Floor" rotation={[Math.PI / 2, 0, 0]}> <mesh name="Wall-Floor" rotation={[Math.PI / 2, 0, 0]}>
<Extrude <Extrude receiveShadow castShadow name="Wall-Floor" args={[shape, { depth: Constants.floorConfig.height, bevelEnabled: false }]} position={[0, 0, 0]}>
receiveShadow
castShadow
name="Wall-Floor"
args={[shape, { depth: Constants.floorConfig.height, bevelEnabled: false }]}
position={[0, 0, 0]}
>
<meshStandardMaterial color={Constants.floorConfig.defaultColor} map={floorTexture} side={DoubleSide} /> <meshStandardMaterial color={Constants.floorConfig.defaultColor} map={floorTexture} side={DoubleSide} />
</Extrude> </Extrude>
</mesh> </mesh>
); );
} }
function Floor2D({ room }: { readonly room: Point[] }) {
const savedTheme: string | null = localStorage.getItem("theme");
const points2D = useMemo(() => {
return room.map((p) => new Vector2(parseFloat(p.position[0].toFixed(2)), parseFloat(p.position[2].toFixed(2))));
}, [room]);
const shape = useMemo(() => {
const shape = new Shape();
shape.moveTo(points2D[0].x, points2D[0].y);
for (let i = 1; i < points2D.length; i++) {
shape.lineTo(points2D[i].x, points2D[i].y);
}
shape.lineTo(points2D[0].x, points2D[0].y);
return shape;
}, [points2D]);
const area = useMemo(() => getArea(points2D), [points2D]);
const centroid: [number, number, number] = useMemo(() => {
const center = getCenteroidPoint(points2D);
if (!center) return [0, Constants.floorConfig.height + 0.01, 0];
return [center.x, Constants.floorConfig.height + 0.01, center.y] as [number, number, number];
}, [points2D]);
if (!shape) return null;
const formattedArea = `${area.toFixed(2)}`;
return (
<>
<mesh castShadow receiveShadow name="Wall-Floor" rotation={[Math.PI / 2, 0, 0]}>
<Extrude receiveShadow castShadow name="Wall-Floor" args={[shape, { depth: Constants.floorConfig.height }]} position={[0, 0, 0]}>
<meshBasicMaterial color={savedTheme === "dark" ? Constants.lineConfig.wallColor : Constants.lineConfig.wallColor} side={DoubleSide} transparent opacity={0.4} depthWrite={false} />
</Extrude>
</mesh>
<Html position={centroid} wrapperClass="distance-text-wrapper" className="distance-text" zIndexRange={[1, 0]} prepend center sprite>
<div className="distance area">({formattedArea})</div>
</Html>
</>
);
}

View File

@@ -1,50 +1,55 @@
import { useMemo } from 'react'; import { useMemo } from "react";
import { DoubleSide, Shape, Vector2 } from 'three'; import { DoubleSide, Shape, Vector2 } from "three";
import { Extrude } from '@react-three/drei'; import { Extrude, Html } from "@react-three/drei";
import * as Constants from '../../../../../types/world/worldConstants'; import * as Constants from "../../../../../types/world/worldConstants";
import getCenteroid from "../../../functions/getCenteroid";
import getArea from "../../../functions/getArea";
function Zone2DInstance({ zone }: { readonly zone: Zone }) { function Zone2DInstance({ zone }: { readonly zone: Zone }) {
const savedTheme: string | null = localStorage.getItem("theme"); const savedTheme: string | null = localStorage.getItem("theme");
const points2D = useMemo(() => {
return zone.points.map((p) => new Vector2(parseFloat(p.position[0].toFixed(2)), parseFloat(p.position[2].toFixed(2))));
}, [zone]);
const shape = useMemo(() => { const shape = useMemo(() => {
const shape = new Shape(); const shape = new Shape();
const points = zone.points.map(p => new Vector2(p.position[0], p.position[2])); shape.moveTo(points2D[0].x, points2D[0].y);
if (points.length < 3) return null; for (let i = 1; i < points2D.length; i++) {
shape.moveTo(points[0].x, points[0].y); shape.lineTo(points2D[i].x, points2D[i].y);
for (let i = 1; i < points.length; i++) {
shape.lineTo(points[i].x, points[i].y);
} }
shape.lineTo(points2D[0].x, points2D[0].y);
return shape; return shape;
}, [zone]); }, [points2D]);
const area = useMemo(() => getArea(points2D), [points2D]);
const centroid: [number, number, number] = useMemo(() => {
const center = getCenteroid(points2D);
if (!center) return [0, Constants.floorConfig.height + 0.01, 0];
return [center.x, Constants.floorConfig.height + 0.01, center.y] as [number, number, number];
}, [points2D]);
if (!shape) return null; if (!shape) return null;
const formattedArea = `${area.toFixed(2)}`;
return ( return (
<mesh <>
castShadow <mesh castShadow receiveShadow name={`Zone-2D-${zone.zoneUuid}`} rotation={[Math.PI / 2, 0, 0]} position={[0, 0, 0]} userData={zone}>
receiveShadow <Extrude name={`Zone-${zone.zoneUuid}`} args={[shape, { depth: Constants.floorConfig.height }]} userData={zone}>
name={`Zone-2D-${zone.zoneUuid}`} <meshBasicMaterial color={savedTheme === "dark" ? Constants.lineConfig.zoneColor : Constants.lineConfig.zoneColor} side={DoubleSide} transparent opacity={0.4} depthWrite={false} />
rotation={[Math.PI / 2, 0, 0]} </Extrude>
position={[0, 0, 0]} </mesh>
userData={zone}
> <Html key={zone.zoneUuid} position={centroid} wrapperClass="distance-text-wrapper" className="distance-text" zIndexRange={[1, 0]} prepend center sprite>
<Extrude <div className="distance area">
name={`Zone-${zone.zoneUuid}`} {zone.zoneName} ({formattedArea})
args={[shape, { </div>
depth: Constants.floorConfig.height, </Html>
}]} </>
userData={zone}
>
<meshBasicMaterial
color={savedTheme === "dark" ? "#007BFF" : "#007BFF"}
side={DoubleSide}
transparent
opacity={0.4}
depthWrite={false}
/>
</Extrude>
</mesh>
); );
} }
export default Zone2DInstance; export default Zone2DInstance;

View File

@@ -1,12 +1,12 @@
import React, { useEffect, useMemo } from 'react'; import React, { useEffect, useMemo } from "react";
import { Vector3 } from 'three'; import { Vector3 } from "three";
import { Html } from '@react-three/drei'; import { Html } from "@react-three/drei";
import { useSceneContext } from '../../../scene/sceneContext'; import { useSceneContext } from "../../../scene/sceneContext";
import { useToggleView } from '../../../../store/builder/store'; import { useToggleView } from "../../../../store/builder/store";
import Line from '../../line/line'; import Line from "../../line/line";
import Point from '../../point/point'; import Point from "../../point/point";
import ZoneInstance from './Instance/zoneInstance'; import ZoneInstance from "./Instance/zoneInstance";
import Zone2DInstance from './Instance/zone2DInstance'; import Zone2DInstance from "./Instance/zone2DInstance";
function ZoneInstances() { function ZoneInstances() {
const { zoneStore } = useSceneContext(); const { zoneStore } = useSceneContext();
@@ -15,14 +15,14 @@ function ZoneInstances() {
useEffect(() => { useEffect(() => {
// console.log('zones: ', zones); // console.log('zones: ', zones);
}, [zones]) }, [zones]);
const allPoints = useMemo(() => { const allPoints = useMemo(() => {
const points: Point[] = []; const points: Point[] = [];
const seenUuids = new Set<string>(); const seenUuids = new Set<string>();
zones.forEach(zone => { zones.forEach((zone) => {
zone.points.forEach(point => { zone.points.forEach((point) => {
if (!seenUuids.has(point.pointUuid)) { if (!seenUuids.has(point.pointUuid)) {
seenUuids.add(point.pointUuid); seenUuids.add(point.pointUuid);
points.push(point); points.push(point);
@@ -50,7 +50,7 @@ function ZoneInstances() {
lines.push({ lines.push({
start: current, start: current,
end: next, end: next,
key: lineKey key: lineKey,
}); });
} }
} }
@@ -61,9 +61,8 @@ function ZoneInstances() {
return ( return (
<> <>
{!toggleView && zones.length > 0 && ( {!toggleView && zones.length > 0 && (
<mesh name='Zones-Group'> <mesh name="Zones-Group">
{zones.map((zone) => ( {zones.map((zone) => (
<ZoneInstance key={zone.zoneUuid} zone={zone} /> <ZoneInstance key={zone.zoneUuid} zone={zone} />
))} ))}
@@ -71,7 +70,7 @@ function ZoneInstances() {
)} )}
{toggleView && zones.length > 0 && ( {toggleView && zones.length > 0 && (
<mesh name='Zones-2D-Group'> <mesh name="Zones-2D-Group">
{zones.map((zone) => ( {zones.map((zone) => (
<Zone2DInstance key={zone.zoneUuid} zone={zone} /> <Zone2DInstance key={zone.zoneUuid} zone={zone} />
))} ))}
@@ -80,14 +79,13 @@ function ZoneInstances() {
{toggleView && ( {toggleView && (
<> <>
<group name='Zone-Points-Group'> <group name="Zone-Points-Group">
{allPoints.map((point) => ( {allPoints.map((point) => (
<Point key={point.pointUuid} point={point} /> <Point key={point.pointUuid} point={point} />
))} ))}
</group> </group>
<group name='Zone-Lines-Group'> <group name="Zone-Lines-Group">
{allLines.map(({ start, end, key }) => ( {allLines.map(({ start, end, key }) => (
<Line key={key} points={[start, end]} /> <Line key={key} points={[start, end]} />
))} ))}
@@ -99,7 +97,7 @@ function ZoneInstances() {
return ( return (
<React.Fragment key={key}> <React.Fragment key={key}>
{toggleView && {toggleView && (
<Html <Html
key={`${start.pointUuid}_${end.pointUuid}`} key={`${start.pointUuid}_${end.pointUuid}`}
userData={line} userData={line}
@@ -110,23 +108,19 @@ function ZoneInstances() {
prepend prepend
sprite sprite
> >
<div <div key={key} className={`distance ${key}`}>
key={key}
className={`distance ${key}`}
>
{distance.toFixed(2)} m {distance.toFixed(2)} m
</div> </div>
</Html> </Html>
} )}
</React.Fragment> </React.Fragment>
) );
})} })}
</group> </group>
</> </>
)} )}
</> </>
) );
} }
export default ZoneInstances export default ZoneInstances;

View File

@@ -71,11 +71,6 @@ export const useToggleView = create<any>((set: any) => ({
setToggleView: (x: any) => set(() => ({ toggleView: x })), setToggleView: (x: any) => set(() => ({ toggleView: x })),
})); }));
export const useRoomsState = create<any>((set: any) => ({
roomsState: [],
setRoomsState: (x: any) => set(() => ({ roomsState: x })),
}));
export const useSelectedItem = create<any>((set: any) => ({ export const useSelectedItem = create<any>((set: any) => ({
selectedItem: { selectedItem: {
name: "", name: "",
@@ -315,11 +310,6 @@ export const useTileDistance = create<any>((set: any) => ({
})), })),
})); }));
export const usePlayAgv = create<any>((set, get) => ({
PlayAgv: [],
setPlayAgv: (updateFn: (prev: any[]) => any[]) => set({ PlayAgv: updateFn(get().PlayAgv) }),
}));
// Define the Asset type // Define the Asset type
type Asset = { type Asset = {
id: string; id: string;
@@ -350,19 +340,6 @@ export const useResourceManagementId = create<ResourceManagementState>((set) =>
setResourceManagementId: (id: string) => set({ resourceManagementId: id }), setResourceManagementId: (id: string) => set({ resourceManagementId: id }),
})); }));
// version visible hidden
interface VersionHistoryState {
viewVersionHistory: boolean;
setVersionHistoryVisible: (value: boolean) => void;
}
const useVersionHistoryVisibleStore = create<VersionHistoryState>((set) => ({
viewVersionHistory: false,
setVersionHistoryVisible: (value) => set({ viewVersionHistory: value }),
}));
export default useVersionHistoryVisibleStore;
interface ShortcutStore { interface ShortcutStore {
showShortcuts: boolean; showShortcuts: boolean;
setShowShortcuts: (value: boolean) => void; setShowShortcuts: (value: boolean) => void;

View File

@@ -1,14 +1,16 @@
import { create } from 'zustand'; import { create } from "zustand";
import { immer } from 'zustand/middleware/immer'; import { immer } from "zustand/middleware/immer";
interface VersionStore { interface VersionStore {
versionHistory: VersionHistory; versionHistory: VersionHistory;
selectedVersion: Version | null; selectedVersion: Version | null;
viewVersionHistory: boolean;
createNewVersion: boolean; createNewVersion: boolean;
setSelectedVersion: (version: Version) => void; setSelectedVersion: (version: Version) => void;
clearSelectedVersion: () => void; clearSelectedVersion: () => void;
setVersionHistoryVisible: (visibility: boolean) => void;
setCreateNewVersion: (createNewVersion: boolean) => void; setCreateNewVersion: (createNewVersion: boolean) => void;
addVersion: (version: Version) => void; addVersion: (version: Version) => void;
@@ -26,6 +28,7 @@ export const createVersionStore = () => {
immer((set, get) => ({ immer((set, get) => ({
versionHistory: [], versionHistory: [],
selectedVersion: null, selectedVersion: null,
viewVersionHistory: false,
createNewVersion: false, createNewVersion: false,
setSelectedVersion: (version) => { setSelectedVersion: (version) => {
@@ -40,30 +43,36 @@ export const createVersionStore = () => {
}); });
}, },
setVersionHistoryVisible: (visibility: boolean) => {
set((state) => {
state.viewVersionHistory = visibility;
});
},
setCreateNewVersion: (createNewVersion: boolean) => { setCreateNewVersion: (createNewVersion: boolean) => {
set((state) => { set((state) => {
state.createNewVersion = createNewVersion; state.createNewVersion = createNewVersion;
}) });
}, },
addVersion: (version: Version) => { addVersion: (version: Version) => {
set((state) => { set((state) => {
state.versionHistory.unshift(version); state.versionHistory.unshift(version);
}) });
}, },
setVersions: (versions: Version[]) => { setVersions: (versions: Version[]) => {
set((state) => { set((state) => {
state.versionHistory = versions; state.versionHistory = versions;
}) });
}, },
clearVersions: () => { clearVersions: () => {
set((state) => { set((state) => {
state.versionHistory = []; state.versionHistory = [];
state.selectedVersion = null; state.selectedVersion = null;
state.createNewVersion = false state.createNewVersion = false;
}) });
}, },
setVersionName: (versionId: string, versionName: string) => { setVersionName: (versionId: string, versionName: string) => {
@@ -72,7 +81,7 @@ export const createVersionStore = () => {
if (version) { if (version) {
version.versionName = versionName; version.versionName = versionName;
} }
}) });
}, },
updateVersion: (versionId: string, versionName: string, versionDescription: string) => { updateVersion: (versionId: string, versionName: string, versionDescription: string) => {
@@ -82,16 +91,16 @@ export const createVersionStore = () => {
version.versionName = versionName; version.versionName = versionName;
version.versionDescription = versionDescription; version.versionDescription = versionDescription;
} }
}) });
}, },
getVersionById: (versionId: string) => { getVersionById: (versionId: string) => {
return get().versionHistory.find((v) => { return get().versionHistory.find((v) => {
return v.versionId === versionId return v.versionId === versionId;
}) });
} },
})) }))
); );
}; };
export type VersionStoreType = ReturnType<typeof createVersionStore>; export type VersionStoreType = ReturnType<typeof createVersionStore>;

View File

@@ -202,7 +202,7 @@ export const firstPersonControls: Controls = {
leftSpeed: -0.1, // Speed of left movement leftSpeed: -0.1, // Speed of left movement
rightSpeed: 0.1, // Speed of right movement rightSpeed: 0.1, // Speed of right movement
walkSpeed: 1, // Walk speed walkSpeed: 1, // Walk speed
sprintSpeed: 4 // Sprint Speed sprintSpeed: 4, // Sprint Speed
}; };
export const thirdPersonControls: ThirdPersonControls = { export const thirdPersonControls: ThirdPersonControls = {
@@ -359,7 +359,7 @@ export const roofConfig: RoofConfig = {
export const aisleConfig: AisleConfig = { export const aisleConfig: AisleConfig = {
width: 0.1, // Width of the aisles width: 0.1, // Width of the aisles
height: 0.01, // Height of the aisles height: 0.01, // Height of the aisles
defaultColor: '#E2AC09', // Default color of the aisles defaultColor: "#E2AC09", // Default color of the aisles
}; };
export const zoneConfig: ZoneConfig = { export const zoneConfig: ZoneConfig = {
@@ -384,4 +384,4 @@ export const distanceConfig: DistanceConfig = {
export const undoRedoConfig: undoRedoCount = { export const undoRedoConfig: undoRedoCount = {
undoRedoCount: 50, undoRedoCount: 50,
} };

View File

@@ -1,7 +1,19 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import useModuleStore, { useSubModuleStore, useThreeDStore } from "../../store/ui/useModuleStore"; import useModuleStore, { useSubModuleStore, useThreeDStore } from "../../store/ui/useModuleStore";
import { usePlayerStore, useToggleStore } from "../../store/ui/useUIToggleStore"; import { usePlayerStore, useToggleStore } from "../../store/ui/useUIToggleStore";
import useVersionHistoryVisibleStore, { useActiveSubTool, useActiveTool, useAddAction, useDfxUpload, useRenameModeStore, useIsComparing, useSelectedComment, useShortcutStore, useToggleView, useToolMode, useViewSceneStore } from "../../store/builder/store"; import {
useActiveSubTool,
useActiveTool,
useAddAction,
useDfxUpload,
useRenameModeStore,
useIsComparing,
useSelectedComment,
useShortcutStore,
useToggleView,
useToolMode,
useViewSceneStore,
} from "../../store/builder/store";
import useCameraModeStore, { usePlayButtonStore } from "../../store/ui/usePlayButtonStore"; import useCameraModeStore, { usePlayButtonStore } from "../../store/ui/usePlayButtonStore";
import { detectModifierKeys } from "./detectModifierKeys"; import { detectModifierKeys } from "./detectModifierKeys";
import { useSelectedZoneStore } from "../../store/visualization/useZoneStore"; import { useSelectedZoneStore } from "../../store/visualization/useZoneStore";
@@ -33,8 +45,7 @@ const KeyPressListener: React.FC = () => {
const { setViewSceneLabels } = useViewSceneStore(); const { setViewSceneLabels } = useViewSceneStore();
const { isRenameMode, setIsRenameMode } = useRenameModeStore(); const { isRenameMode, setIsRenameMode } = useRenameModeStore();
const { selectedFloorAsset, setSelectedWallAsset } = useBuilderStore(); const { selectedFloorAsset, setSelectedWallAsset } = useBuilderStore();
const { setCreateNewVersion } = versionStore(); const { setCreateNewVersion, setVersionHistoryVisible } = versionStore();
const { setVersionHistoryVisible } = useVersionHistoryVisibleStore();
const { setSelectedComment } = useSelectedComment(); const { setSelectedComment } = useSelectedComment();
const { setDfxUploaded } = useDfxUpload(); const { setDfxUploaded } = useDfxUpload();
const isTextInput = (element: Element | null): boolean => element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element?.getAttribute("contenteditable") === "true"; const isTextInput = (element: Element | null): boolean => element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element?.getAttribute("contenteditable") === "true";