optimized old zustand store and post processing outlines

This commit is contained in:
2025-09-01 15:09:04 +05:30
parent 09c909c377
commit ef98b3c1a3
19 changed files with 1228 additions and 1440 deletions

View File

@@ -1,13 +1,12 @@
import React, { useEffect } from "react";
import {
useLoadingProgress,
useRenameModeStore,
useSaveVersion,
useSelectedAssets,
useSelectedComment,
useSelectedFloorItem,
useSocketStore,
useWidgetSubOption,
useLoadingProgress,
useRenameModeStore,
useSaveVersion,
useSelectedAssets,
useSelectedComment,
useSocketStore,
useWidgetSubOption,
} from "../../../store/builder/store";
import useModuleStore, { useThreeDStore } from "../../../store/useModuleStore";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
@@ -27,10 +26,7 @@ import ControlsPlayer from "../controls/ControlsPlayer";
import SelectFloorPlan from "../../temporary/SelectFloorPlan";
import { createHandleDrop } from "../../../modules/visualization/functions/handleUiDrop";
import Scene from "../../../modules/scene/scene";
import {
useComparisonProduct,
useMainProduct,
} from "../../../store/simulation/useSimulationStore";
import { useComparisonProduct, useMainProduct } from "../../../store/simulation/useSimulationStore";
import { useProductContext } from "../../../modules/simulation/products/productContext";
import RegularDropDown from "../../ui/inputs/RegularDropDown";
import RenameTooltip from "../../ui/features/RenameTooltip";
@@ -42,184 +38,173 @@ import { useVersionContext } from "../../../modules/builder/version/versionConte
import VersionSaved from "../sidebarRight/versionHisory/VersionSaved";
import Footer from "../../footer/Footer";
import ThreadChat from "../../ui/collaboration/ThreadChat";
import { useBuilderStore } from "../../../store/builder/useBuilderStore";
function MainScene() {
const { setMainProduct } = useMainProduct();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { isVersionSaved, setIsVersionSaved } = useSaveVersion();
const { activeModule } = useModuleStore();
const { selectedUser } = useSelectedUserStore();
const { loadingProgress } = useLoadingProgress();
const { toggleThreeD } = useThreeDStore();
const { isPlaying } = usePlayButtonStore();
const { widgetSubOption } = useWidgetSubOption();
const { visualizationSocket } = useSocketStore();
const { selectedZone } = useSelectedZoneStore();
const { setFloatingWidget } = useFloatingWidget();
const { clearComparisonProduct } = useComparisonProduct();
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
const { assetStore, productStore } = useSceneContext();
const { products } = productStore();
const { setName } = assetStore();
const { projectId } = useParams()
const { isRenameMode, setIsRenameMode } = useRenameModeStore();
const { versionHistory } = useVersionHistoryStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion, setSelectedVersion } = selectedVersionStore();
const { selectedComment, commentPositionState } = useSelectedComment();
const { setMainProduct } = useMainProduct();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { isVersionSaved, setIsVersionSaved } = useSaveVersion();
const { activeModule } = useModuleStore();
const { selectedUser } = useSelectedUserStore();
const { loadingProgress } = useLoadingProgress();
const { toggleThreeD } = useThreeDStore();
const { isPlaying } = usePlayButtonStore();
const { widgetSubOption } = useWidgetSubOption();
const { visualizationSocket } = useSocketStore();
const { selectedZone } = useSelectedZoneStore();
const { setFloatingWidget } = useFloatingWidget();
const { clearComparisonProduct } = useComparisonProduct();
const { selectedFloorAsset, setSelectedFloorAsset } = useBuilderStore();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
const { assetStore, productStore } = useSceneContext();
const { products } = productStore();
const { setName } = assetStore();
const { projectId } = useParams()
const { isRenameMode, setIsRenameMode } = useRenameModeStore();
const { versionHistory } = useVersionHistoryStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion, setSelectedVersion } = selectedVersionStore();
const { selectedComment, commentPositionState } = useSelectedComment();
useEffect(() => {
if (activeModule !== 'simulation') {
clearComparisonProduct();
setIsVersionSaved(false);
}
}, [activeModule, clearComparisonProduct, setIsVersionSaved])
useEffect(() => {
if (versionHistory.length > 0) {
setSelectedVersion(versionHistory[0])
}
}, [setSelectedVersion, versionHistory])
const handleSelectVersion = (option: string) => {
const version = versionHistory.find((version) => version.versionName === option);
if (version) {
setSelectedVersion(version);
}
};
const handleSelectProduct = (option: string) => {
const product = products.find((product) => product.productName === option);
if (product) {
setMainProduct(product.productUuid, product.productName);
}
};
const handleObjectRename = async (newName: string) => {
if (!projectId) return
if (selectedFloorItem) {
console.log('selectedFloorItem.userData.modelUuid: ', selectedFloorItem.userData.modelUuid);
console.log(' newName: ', newName);
console.log('projectId: ', projectId);
setAssetsApi({
modelUuid: selectedFloorItem.userData.modelUuid,
modelName: newName,
projectId,
versionId: selectedVersion?.versionId || ''
}).then(() => {
selectedFloorItem.userData = {
...selectedFloorItem.userData,
modelName: newName
};
setSelectedFloorItem(selectedFloorItem);
setIsRenameMode(false);
setName(selectedFloorItem.userData.modelUuid, newName);
})
} else if (selectedAssets.length === 1) {
setAssetsApi({
modelUuid: selectedAssets[0].userData.modelUuid,
modelName: newName,
projectId,
versionId: selectedVersion?.versionId || ''
}).then(() => {
selectedAssets[0].userData = {
...selectedAssets[0].userData,
modelName: newName
};
setSelectedAssets(selectedAssets);
setIsRenameMode(false);
setName(selectedAssets[0].userData.modelUuid, newName);
})
}
}
return (
<>
{!selectedUser && (
<>
<KeyPressListener />
{loadingProgress > 0 && <LoadingPage progress={loadingProgress} />}
{!isPlaying && (
<>
{toggleThreeD && !isVersionSaved && <ModuleToggle />}
<SideBarLeft />
<SideBarRight />
</>
)}
<RealTimeVisulization />
{activeModule === "market" && <MarketPlace />}
{activeModule !== "market" && !isPlaying && !isVersionSaved && (
<Tools />
)}
{(isPlaying) &&
activeModule === "simulation" &&
loadingProgress === 0 && <SimulationPlayer />}
{(isPlaying) &&
activeModule !== "simulation" && <ControlsPlayer />}
{isRenameMode && (selectedFloorItem?.userData.modelName || selectedAssets.length === 1) && <RenameTooltip name={selectedFloorItem?.userData.modelName || selectedAssets[0].userData.modelName} onSubmit={handleObjectRename} />}
{/* remove this later */}
{activeModule === "builder" && !toggleThreeD && <SelectFloorPlan />}
</>
)}
<div
className="scene-container"
id="work-space-three-d-canvas"
style={{
height: isPlaying || activeModule !== "visualization" ? "100vh" : "",
width: isPlaying || activeModule !== "visualization" ? "100vw" : "",
left: isPlaying || activeModule !== "visualization" ? "0%" : "",
borderRadius:
isPlaying || activeModule !== "visualization" ? "" : "6px",
}}
role="application"
onDrop={(event) =>
createHandleDrop({
widgetSubOption,
visualizationSocket,
selectedZone,
setFloatingWidget,
event,
projectId,
versionId: selectedVersion?.versionId || '',
})
useEffect(() => {
if (activeModule !== 'simulation') {
clearComparisonProduct();
setIsVersionSaved(false);
}
onDragOver={(event) => event.preventDefault()}
>
<Scene layout="Main Layout" />
</div>
}, [activeModule, clearComparisonProduct, setIsVersionSaved])
{selectedProduct && selectedVersion && isVersionSaved && !isPlaying && activeModule === "simulation" && (
<div className="selectLayout-wrapper">
<RegularDropDown
header={selectedVersion.versionName}
options={versionHistory.map((v) => v.versionName)} // Pass layout names as options
onSelect={handleSelectVersion}
search={false}
/>
<br />
<RegularDropDown
header={selectedProduct.productName}
options={products.map((l) => l.productName)} // Pass layout names as options
onSelect={handleSelectProduct}
search={false}
/>
</div>
)}
useEffect(() => {
if (versionHistory.length > 0) {
setSelectedVersion(versionHistory[0])
}
}, [setSelectedVersion, versionHistory])
{activeModule !== "market" && !selectedUser && <Footer />}
const handleSelectVersion = (option: string) => {
const version = versionHistory.find((version) => version.versionName === option);
if (version) {
setSelectedVersion(version);
}
};
<VersionSaved />
const handleSelectProduct = (option: string) => {
const product = products.find((product) => product.productName === option);
if (product) {
setMainProduct(product.productUuid, product.productName);
}
};
{
(commentPositionState !== null || selectedComment !== null) &&
<ThreadChat />
}
const handleObjectRename = async (newName: string) => {
if (!projectId) return
if (selectedFloorAsset) {
setAssetsApi({
modelUuid: selectedFloorAsset.userData.modelUuid,
modelName: newName,
projectId,
versionId: selectedVersion?.versionId || ''
}).then(() => {
selectedFloorAsset.userData = { ...selectedFloorAsset.userData, modelName: newName };
setSelectedFloorAsset(selectedFloorAsset);
setIsRenameMode(false);
setName(selectedFloorAsset.userData.modelUuid, newName);
})
} else if (selectedAssets.length === 1) {
setAssetsApi({
modelUuid: selectedAssets[0].userData.modelUuid,
modelName: newName,
projectId,
versionId: selectedVersion?.versionId || ''
}).then(() => {
selectedAssets[0].userData = { ...selectedAssets[0].userData, modelName: newName };
setSelectedAssets(selectedAssets);
setIsRenameMode(false);
setName(selectedAssets[0].userData.modelUuid, newName);
})
}
}
</>
);
return (
<>
{!selectedUser && (
<>
<KeyPressListener />
{loadingProgress > 0 && <LoadingPage progress={loadingProgress} />}
{!isPlaying && (
<>
{toggleThreeD && !isVersionSaved && <ModuleToggle />}
<SideBarLeft />
<SideBarRight />
</>
)}
<RealTimeVisulization />
{activeModule === "market" && <MarketPlace />}
{activeModule !== "market" && !isPlaying && !isVersionSaved && (
<Tools />
)}
{(isPlaying) && activeModule === "simulation" && loadingProgress === 0 && <SimulationPlayer />}
{(isPlaying) && activeModule !== "simulation" && <ControlsPlayer />}
{isRenameMode && (selectedFloorAsset?.userData.modelName || selectedAssets.length === 1) && <RenameTooltip name={selectedFloorAsset?.userData.modelName || selectedAssets[0].userData.modelName} onSubmit={handleObjectRename} />}
{/* remove this later */}
{activeModule === "builder" && !toggleThreeD && <SelectFloorPlan />}
</>
)}
<div
className="scene-container"
id="work-space-three-d-canvas"
style={{
height: isPlaying || activeModule !== "visualization" ? "100vh" : "",
width: isPlaying || activeModule !== "visualization" ? "100vw" : "",
left: isPlaying || activeModule !== "visualization" ? "0%" : "",
borderRadius:
isPlaying || activeModule !== "visualization" ? "" : "6px",
}}
role="application"
onDrop={(event) =>
createHandleDrop({
widgetSubOption,
visualizationSocket,
selectedZone,
setFloatingWidget,
event,
projectId,
versionId: selectedVersion?.versionId || '',
})
}
onDragOver={(event) => event.preventDefault()}
>
<Scene layout="Main Layout" />
</div>
{selectedProduct && selectedVersion && isVersionSaved && !isPlaying && activeModule === "simulation" && (
<div className="selectLayout-wrapper">
<RegularDropDown
header={selectedVersion.versionName}
options={versionHistory.map((v) => v.versionName)} // Pass layout names as options
onSelect={handleSelectVersion}
search={false}
/>
<br />
<RegularDropDown
header={selectedProduct.productName}
options={products.map((l) => l.productName)} // Pass layout names as options
onSelect={handleSelectProduct}
search={false}
/>
</div>
)}
{activeModule !== "market" && !selectedUser && <Footer />}
<VersionSaved />
{
(commentPositionState !== null || selectedComment !== null) &&
<ThreadChat />
}
</>
);
}
export default MainScene;

View File

@@ -6,7 +6,7 @@ import { useToggleStore } from "../../../store/useUIToggleStore";
import Visualization from "./visualization/Visualization";
import Analysis from "./analysis/Analysis";
import Simulations from "./simulation/Simulations";
import useVersionHistoryVisibleStore, { useSaveVersion, useSelectedFloorItem, useToolMode } from "../../../store/builder/store";
import useVersionHistoryVisibleStore, { useSaveVersion, useToolMode } from "../../../store/builder/store";
import { useSelectedEventData, useSelectedEventSphere, } from "../../../store/simulation/useSimulationStore";
import { useBuilderStore } from "../../../store/builder/useBuilderStore";
import GlobalProperties from "./properties/GlobalProperties";
@@ -48,8 +48,7 @@ const SideBarRight: React.FC = () => {
const { toggleUIRight } = useToggleStore();
const { toolMode } = useToolMode();
const { subModule, setSubModule } = useSubModuleStore();
const { selectedFloorItem } = useSelectedFloorItem();
const { selectedWall, selectedFloor, selectedAisle } = useBuilderStore();
const { selectedWall, selectedFloor, selectedAisle, selectedFloorAsset } = useBuilderStore();
const { selectedEventData } = useSelectedEventData();
const { selectedEventSphere } = useSelectedEventSphere();
const { viewVersionHistory, setVersionHistoryVisible } = useVersionHistoryVisibleStore();
@@ -108,31 +107,31 @@ const SideBarRight: React.FC = () => {
}
if (subModule === "properties" && activeModule !== "visualization") {
if (selectedFloorItem) {
if (selectedFloorAsset) {
setDisplayComponent("assetProperties");
return;
}
if (!selectedFloorItem && !selectedFloor && !selectedAisle && !selectedDecal && selectedWall) {
if (!selectedFloorAsset && !selectedFloor && !selectedAisle && !selectedDecal && selectedWall) {
setDisplayComponent("selectedWallProperties");
return;
}
if (!selectedFloorItem && !selectedWall && !selectedAisle && !selectedDecal && selectedFloor) {
if (!selectedFloorAsset && !selectedWall && !selectedAisle && !selectedDecal && selectedFloor) {
setDisplayComponent("selectedFloorProperties");
return;
}
if (viewVersionHistory && !selectedFloorItem && !selectedWall && !selectedAisle && !selectedFloor && !selectedDecal) {
if (viewVersionHistory && !selectedFloorAsset && !selectedWall && !selectedAisle && !selectedFloor && !selectedDecal) {
setDisplayComponent("versionHistory");
return;
}
if (!selectedFloorItem && !selectedFloor && !selectedAisle && !selectedWall && selectedDecal) {
if (!selectedFloorAsset && !selectedFloor && !selectedAisle && !selectedWall && selectedDecal) {
setDisplayComponent("selectedDecalProperties");
return;
}
if (!selectedFloorItem && !selectedFloor && !selectedWall && !selectedDecal && selectedAisle) {
if (!selectedFloorAsset && !selectedFloor && !selectedWall && !selectedDecal && selectedAisle) {
setDisplayComponent("selectedAisleProperties");
return;
}
if (!selectedFloorItem && !selectedFloor && !selectedWall && !selectedDecal && !selectedAisle) {
if (!selectedFloorAsset && !selectedFloor && !selectedWall && !selectedDecal && !selectedAisle) {
if (toolMode === "Aisle") {
setDisplayComponent("aisleProperties");
return;
@@ -156,7 +155,7 @@ const SideBarRight: React.FC = () => {
}
setDisplayComponent("none");
}, [viewVersionHistory, activeModule, subModule, isVersionSaved, selectedFloorItem, selectedWall, selectedFloor, selectedAisle, toolMode, selectedDecal]);
}, [viewVersionHistory, activeModule, subModule, isVersionSaved, selectedFloorAsset, selectedWall, selectedFloor, selectedAisle, toolMode, selectedDecal]);
const renderComponent = () => {
switch (displayComponent) {

View File

@@ -4,143 +4,138 @@ import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import { RemoveIcon } from "../../../icons/ExportCommonIcons";
import PositionInput from "../customInput/PositionInputs";
import RotationInput from "../customInput/RotationInput";
import {
useSelectedFloorItem,
useObjectPosition,
useObjectRotation,
} from "../../../../store/builder/store";
import { useObjectPosition, useObjectRotation } from "../../../../store/builder/store";
import { useSceneContext } from "../../../../modules/scene/sceneContext";
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
interface UserData {
id: number;
label: string;
value: string;
id: number;
label: string;
value: string;
}
const AssetProperties: React.FC = () => {
const [userData, setUserData] = useState<UserData[]>([]);
const { selectedFloorItem } = useSelectedFloorItem();
const { objectPosition } = useObjectPosition();
const { objectRotation } = useObjectRotation();
const { assetStore } = useSceneContext();
const { assets, setCurrentAnimation } = assetStore();
const { loopAnimation } = useBuilderStore();
const [hoveredIndex, setHoveredIndex] = useState<any>(null);
const [userData, setUserData] = useState<UserData[]>([]);
const { objectPosition } = useObjectPosition();
const { objectRotation } = useObjectRotation();
const { assetStore } = useSceneContext();
const { assets, setCurrentAnimation } = assetStore();
const { loopAnimation, selectedFloorAsset } = useBuilderStore();
const [hoveredIndex, setHoveredIndex] = useState<any>(null);
const handleAddUserData = () => {
setUserData([]);
};
const handleAddUserData = () => {
setUserData([]);
};
const handleUserDataChange = (id: number, newValue: string) => { };
const handleUserDataChange = (id: number, newValue: string) => { };
const handleRemoveUserData = (id: number) => { };
const handleRemoveUserData = (id: number) => { };
const handleAnimationClick = (animation: string) => {
if (selectedFloorItem) {
setCurrentAnimation(
selectedFloorItem.uuid,
animation,
true,
loopAnimation,
true
);
}
};
if (!selectedFloorItem) return null;
return (
<div className="asset-properties-container">
{/* Name */}
<div className="header">{selectedFloorItem.userData.modelName}</div>
<section>
{objectPosition && (
<PositionInput
disabled={true}
onChange={() => { }}
value1={parseFloat(objectPosition.x.toFixed(5))}
value2={parseFloat(objectPosition.z.toFixed(5))}
/>
)}
{objectRotation && (
<RotationInput
disabled={true}
onChange={() => { }}
value={parseFloat(objectRotation.y.toFixed(5))}
/>
)}
</section>
<div className="header">Render settings</div>
<section>
<InputToggle inputKey="visible" label="Visible" />
<InputToggle inputKey="frustumCull" label="Frustum cull" />
</section>
<section>
<div className="header">User Data</div>
{userData.map((data, i) => (
<div className="input-container" key={i}>
<InputWithDropDown
key={data.id}
label={data.label}
value={data.value}
editableLabel
onChange={(newValue) => handleUserDataChange(data.id, newValue)}
/>
<div
className="remove-button"
onClick={() => handleRemoveUserData(data.id)}
>
<RemoveIcon />
</div>
</div>
))}
{/* Add new user data */}
<div className="optimize-button" onClick={handleAddUserData}>
+ Add
</div>
</section>
<div className="header">Animations</div>
<section className="animations-lists">
{assets.map((asset, i) => {
if (asset.modelUuid !== selectedFloorItem.uuid || !asset.animations)
return (
i === 0 && (
<div className="no-animation" key={i}>
Looks like there are no preset animations yet. Stay tuned for
future additions!
</div>
)
const handleAnimationClick = (animation: string) => {
if (selectedFloorAsset) {
setCurrentAnimation(
selectedFloorAsset.uuid,
animation,
true,
loopAnimation,
true
);
}
};
return asset.animations.map((animation, index) => (
<div key={index} className="animations-list-wrapper">
<div
onClick={() => handleAnimationClick(animation)}
onMouseEnter={() => setHoveredIndex(index)}
onMouseLeave={() => setHoveredIndex(null)}
className="animations-list"
style={{
background:
hoveredIndex === index
? "var(--background-color-button)"
: "var(--background-color)",
color:
hoveredIndex === index ? "var(--text-button-color)" : "",
}}
>
{animation.charAt(0).toUpperCase() +
animation.slice(1).toLowerCase()}
</div>
</div>
));
})}
</section>
</div>
);
if (!selectedFloorAsset) return null;
return (
<div className="asset-properties-container">
{/* Name */}
<div className="header">{selectedFloorAsset.userData.modelName}</div>
<section>
{objectPosition && (
<PositionInput
disabled={true}
onChange={() => { }}
value1={parseFloat(objectPosition.x.toFixed(5))}
value2={parseFloat(objectPosition.z.toFixed(5))}
/>
)}
{objectRotation && (
<RotationInput
disabled={true}
onChange={() => { }}
value={parseFloat(objectRotation.y.toFixed(5))}
/>
)}
</section>
<div className="header">Render settings</div>
<section>
<InputToggle inputKey="visible" label="Visible" />
<InputToggle inputKey="frustumCull" label="Frustum cull" />
</section>
<section>
<div className="header">User Data</div>
{userData.map((data, i) => (
<div className="input-container" key={i}>
<InputWithDropDown
key={data.id}
label={data.label}
value={data.value}
editableLabel
onChange={(newValue) => handleUserDataChange(data.id, newValue)}
/>
<div
className="remove-button"
onClick={() => handleRemoveUserData(data.id)}
>
<RemoveIcon />
</div>
</div>
))}
{/* Add new user data */}
<div className="optimize-button" onClick={handleAddUserData}>
+ Add
</div>
</section>
<div className="header">Animations</div>
<section className="animations-lists">
{assets.map((asset, i) => {
if (asset.modelUuid !== selectedFloorAsset.uuid || !asset.animations)
return (
i === 0 && (
<div className="no-animation" key={i}>
Looks like there are no preset animations yet. Stay tuned for
future additions!
</div>
)
);
return asset.animations.map((animation, index) => (
<div key={index} className="animations-list-wrapper">
<div
onClick={() => handleAnimationClick(animation)}
onMouseEnter={() => setHoveredIndex(index)}
onMouseLeave={() => setHoveredIndex(null)}
className="animations-list"
style={{
background:
hoveredIndex === index
? "var(--background-color-button)"
: "var(--background-color)",
color:
hoveredIndex === index ? "var(--text-button-color)" : "",
}}
>
{animation.charAt(0).toUpperCase() +
animation.slice(1).toLowerCase()}
</div>
</div>
));
})}
</section>
</div>
);
};
export default AssetProperties;

View File

@@ -3,27 +3,13 @@ import InputRange from "../../../ui/inputs/InputRange";
import InputToggle from "../../../ui/inputs/InputToggle";
import { AIIcon } from "../../../icons/ExportCommonIcons";
import LabeledButton from "../../../ui/inputs/LabledButton";
import {
useAzimuth,
useElevation,
useLimitDistance,
useRenderDistance,
useResetCamera,
useRoofVisibility,
useSelectedWallItem,
useShadows,
useSocketStore,
useTileDistance,
useToggleView,
useWallVisibility,
} from "../../../../store/builder/store";
import { useAzimuth, useElevation, useLimitDistance, useRenderDistance, useResetCamera, useRoofVisibility, useShadows, useSocketStore, useTileDistance, useToggleView, useWallVisibility } from "../../../../store/builder/store";
import { setEnvironment } from "../../../../services/factoryBuilder/environment/setEnvironment";
import * as CONSTANTS from "../../../../types/world/worldConstants";
import { useParams } from "react-router-dom";
import { getUserData } from "../../../../functions/getUserData";
const GlobalProperties: React.FC = () => {
const { toggleView, setToggleView } = useToggleView();
const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
const { roofVisibility, setRoofVisibility } = useRoofVisibility();
const { wallVisibility, setWallVisibility } = useWallVisibility();
const { shadows, setShadows } = useShadows();
@@ -90,16 +76,6 @@ const GlobalProperties: React.FC = () => {
setDistance(value);
setRenderDistance(value);
}
function updateGridDistance(value: number) {
setGridDistance(value);
// setGridValue({ size: value * 100, divisions: (value * 100) / 4 });
// setPlaneValue({ height: value * 100, width: value * 100 });
}
function updatedGrid(value: number) {
// console.log(" (value * 100) / 4 : ", (value * 100) / 4);
setGridValue({ size: value * 100, divisions: (value * 100) / 4 });
setPlaneValue({ height: value * 100, width: value * 100 });
}
const updatedDist = async (value: number) => {
setRenderDistance(value);
@@ -145,7 +121,7 @@ const GlobalProperties: React.FC = () => {
setRoofVisibility(!roofVisibility); // Toggle roof visibility
};
// Function to toggle wall visibility
const changeWallVisibility = async () => {
//using REST
const data = await setEnvironment(
@@ -203,20 +179,10 @@ const GlobalProperties: React.FC = () => {
const toggleResetCamera = () => {
if (!toggleView) {
setResetCamera(true); // Trigger reset camera action
setResetCamera(true);
}
};
// function changeRenderDistance(e: any) {
// if (parseInt(e.target.value) < 20) {
// setRenderDistance(20);
// } else if (parseInt(e.target.value) > 75) {
// setRenderDistance(75);
// } else {
// setRenderDistance(parseInt(e.target.value));
// }
// }
return (
<div className="global-properties-container">
<section>
@@ -253,16 +219,10 @@ const GlobalProperties: React.FC = () => {
/>
<div className="split"></div>
{/* //visibleEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor} */}
<InputToggle
inputKey="4"
label="Limit Render Distance"
value={limitDistance}
// onClick={() => {
// setLimitDistance(!limitDistance);
// // setDistance(75);
// // setRenderDistance(75);
// }}
onClick={async () => {
await limitRenderDistance(); // Call the function here
}}
@@ -277,26 +237,6 @@ const GlobalProperties: React.FC = () => {
onPointerUp={updatedDist}
key={"6"}
/>
{/* <div className="split"></div>
<InputToggle
inputKey="6"
label="Display Grid"
value={limitGridDistance}
onClick={() => {
setLimitGridDistance(!limitGridDistance);
}}
/>
<InputRange
label="Tile Distance"
disabled={!limitGridDistance}
value={gridDistance}
key={"7"}
min={1}
max={5}
onChange={(value: number) => updateGridDistance(value)}
onPointerUp={updatedGrid}
/> */}
</section>
</div>
);

View File

@@ -1,17 +1,17 @@
import React, { useEffect, useRef, useState } from "react";
import {
AsileIcon,
CommentIcon,
CursorIcon,
DeleteIcon,
FloorIcon,
FreeMoveIcon,
MeasureToolIcon,
PenIcon,
PlayIcon,
SaveTemplateIcon,
WallIcon,
ZoneIcon,
AsileIcon,
CommentIcon,
CursorIcon,
DeleteIcon,
FloorIcon,
FreeMoveIcon,
MeasureToolIcon,
PenIcon,
PlayIcon,
SaveTemplateIcon,
WallIcon,
ZoneIcon,
} from "../icons/ExportToolsIcons";
import { ArrowIcon, TickIcon } from "../icons/ExportCommonIcons";
import useModuleStore, { useThreeDStore } from "../../store/useModuleStore";
@@ -20,420 +20,417 @@ import { usePlayButtonStore } from "../../store/usePlayButtonStore";
import useTemplateStore from "../../store/useTemplateStore";
import { useSelectedZoneStore } from "../../store/visualization/useZoneStore";
import {
useActiveTool,
useAddAction,
useSelectedWallItem,
useSocketStore,
useToggleView,
useToolMode,
useActiveSubTool,
useShortcutStore,
useActiveTool,
useAddAction,
useSocketStore,
useToggleView,
useToolMode,
useActiveSubTool,
useShortcutStore,
} from "../../store/builder/store";
import { useToggleStore } from "../../store/useUIToggleStore";
import {
use3DWidget,
useFloatingWidget,
use3DWidget,
useFloatingWidget,
} from "../../store/visualization/useDroppedObjectsStore";
import { useParams } from "react-router-dom";
import { useVersionContext } from "../../modules/builder/version/versionContext";
import { MoveIcon, RotateIcon } from "../icons/ShortcutIcons";
import { useBuilderStore } from "../../store/builder/useBuilderStore";
// Utility component
const ToolButton = ({
toolKey,
toolId,
icon: Icon,
active,
onClick,
tooltip,
toolKey,
toolId,
icon: Icon,
active,
onClick,
tooltip,
}: any) => (
<button
key={toolKey} // used in rendering list
id={toolId}
className={`tool-button ${active ? "active" : ""}`}
onClick={onClick}
>
<div className="tooltip">{tooltip}</div>
<div className="tool" id={toolId}>
<Icon isActive={active} />
</div>
</button>
<button
key={toolKey} // used in rendering list
id={toolId}
className={`tool-button ${active ? "active" : ""}`}
onClick={onClick}
>
<div className="tooltip">{tooltip}</div>
<div className="tool" id={toolId}>
<Icon isActive={active} />
</div>
</button>
);
const Tools: React.FC = () => {
const { activeModule } = useModuleStore();
const { toggleThreeD, setToggleThreeD } = useThreeDStore();
const { isPlaying, setIsPlaying } = usePlayButtonStore();
const { showShortcuts } = useShortcutStore();
const { activeModule } = useModuleStore();
const { toggleThreeD, setToggleThreeD } = useThreeDStore();
const { isPlaying, setIsPlaying } = usePlayButtonStore();
const { showShortcuts } = useShortcutStore();
const { activeTool, setActiveTool, setToolMode, setAddAction } = useStoreHooks();
const { setSelectedWallAsset } = useBuilderStore();
const { setActiveSubTool, activeSubTool } = useActiveSubTool();
const { setToggleUI } = useToggleStore();
const { setToggleView, toggleView } = useToggleView();
const { activeTool, setActiveTool, setToolMode, setAddAction } =
useStoreHooks();
const { addTemplate, templates } = useTemplateStore();
const { selectedZone } = useSelectedZoneStore();
const { floatingWidget } = useFloatingWidget();
const { widgets3D } = use3DWidget();
const { setActiveSubTool, activeSubTool } = useActiveSubTool();
const { setSelectedWallItem } = useSelectedWallItem();
const { setToggleUI } = useToggleStore();
const { setToggleView, toggleView } = useToggleView();
const { visualizationSocket } = useSocketStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const { addTemplate, templates } = useTemplateStore();
const { selectedZone } = useSelectedZoneStore();
const { floatingWidget } = useFloatingWidget();
const { widgets3D } = use3DWidget();
const dropdownRef = useRef<HTMLButtonElement>(null);
const [openDrop, setOpenDrop] = useState(false);
const { visualizationSocket } = useSocketStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
// 1. Set UI toggles on initial render
useEffect(() => {
setToggleUI(
localStorage.getItem("navBarUiLeft") !== "false",
localStorage.getItem("navBarUiRight") !== "false"
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const dropdownRef = useRef<HTMLButtonElement>(null);
const [openDrop, setOpenDrop] = useState(false);
// 2. Update tool based on subtool and module
useEffect(() => {
setActiveTool(activeSubTool);
setActiveSubTool(activeSubTool);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeModule]);
// 1. Set UI toggles on initial render
useEffect(() => {
setToggleUI(
localStorage.getItem("navBarUiLeft") !== "false",
localStorage.getItem("navBarUiRight") !== "false"
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// 3. Update tools behavior based on selected tool and view mode
useEffect(() => {
resetTools();
updateToolBehavior(activeTool, toggleView);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeTool, toggleView]);
// 2. Update tool based on subtool and module
useEffect(() => {
setActiveTool(activeSubTool);
setActiveSubTool(activeSubTool);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeModule]);
// 4. Dropdown auto-close
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(e.target as Node)
) {
setOpenDrop(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
// 3. Update tools behavior based on selected tool and view mode
useEffect(() => {
resetTools();
updateToolBehavior(activeTool, toggleView);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeTool, toggleView]);
// 4. Dropdown auto-close
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(e.target as Node)
) {
setOpenDrop(false);
}
const resetTools = () => {
setToolMode(null);
setAddAction(null);
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
const resetTools = () => {
setToolMode(null);
setAddAction(null);
};
const updateToolBehavior = (tool: string, is2D: boolean) => {
switch (tool) {
case "cursor":
is2D ? setToolMode("move") : setToolMode("cursor");
break;
case "draw-wall":
is2D && setToolMode("Wall");
break;
case "draw-aisle":
is2D && setToolMode("Aisle");
break;
case "draw-zone":
is2D && setToolMode("Zone");
break;
case "draw-floor":
is2D && setToolMode("Floor");
break;
case "move":
if (!is2D) setToolMode("Move-Asset");
break;
case "rotate":
if (!is2D) setToolMode("Rotate-Asset");
break;
case "measure":
setToolMode("MeasurementScale");
break;
case "Add pillar":
if (!is2D) setAddAction("Pillar");
break;
case "delete":
is2D ? setToolMode("2D-Delete") : setToolMode("3D-Delete");
break;
}
};
const toggle2D3D = () => {
const toggleTo2D = toggleView;
setToggleView(!toggleTo2D);
setToggleThreeD(toggleTo2D);
setToggleUI(toggleTo2D, toggleTo2D);
if (toggleTo2D) {
setSelectedWallItem(null);
setAddAction(null);
}
setActiveTool("cursor");
setActiveSubTool("cursor");
setToggleUI(
localStorage.getItem("navBarUiLeft") !== "false",
localStorage.getItem("navBarUiRight") !== "false"
);
};
const renderBuilderTools = () => (
<>
{!toggleThreeD && (
<div className="draw-tools">
<ToolButton
toolId="drawWall"
icon={WallIcon}
tooltip="draw wall (q)"
active={activeTool === "draw-wall"}
onClick={() => setActiveTool("draw-wall")}
/>
<ToolButton
toolId="drawZone"
icon={ZoneIcon}
tooltip="draw zone (e)"
active={activeTool === "draw-zone"}
onClick={() => setActiveTool("draw-zone")}
/>
<ToolButton
toolId="drawAisle"
icon={AsileIcon}
tooltip="draw aisle (r)"
active={activeTool === "draw-aisle"}
onClick={() => setActiveTool("draw-aisle")}
/>
<ToolButton
toolId="drawFloor"
icon={FloorIcon}
tooltip="draw floor (t)"
active={activeTool === "draw-floor"}
onClick={() => setActiveTool("draw-floor")}
/>
</div>
)}
<div className="draw-tools">
<ToolButton
toolId="measureScale"
icon={MeasureToolIcon}
tooltip="measure scale (m)"
active={activeTool === "measure"}
onClick={() => setActiveTool("measure")}
/>
</div>
</>
);
const renderSimulationTools = () => (
<div className="draw-tools">
<ToolButton
toolId="pen"
icon={PenIcon}
tooltip="pen"
active={activeTool === "pen"}
onClick={() => setActiveTool("pen")}
/>
</div>
);
const renderVisualizationTools = () => (
<div className="draw-tools">
<ToolButton
toolId="saveTemplate"
icon={SaveTemplateIcon}
tooltip="save template"
active={false}
onClick={() =>
handleSaveTemplate({
addTemplate,
floatingWidget,
widgets3D,
selectedZone,
templates,
visualizationSocket,
projectId,
versionId: selectedVersion?.versionId || "",
})
const updateToolBehavior = (tool: string, is2D: boolean) => {
switch (tool) {
case "cursor":
is2D ? setToolMode("move") : setToolMode("cursor");
break;
case "draw-wall":
is2D && setToolMode("Wall");
break;
case "draw-aisle":
is2D && setToolMode("Aisle");
break;
case "draw-zone":
is2D && setToolMode("Zone");
break;
case "draw-floor":
is2D && setToolMode("Floor");
break;
case "move":
if (!is2D) setToolMode("Move-Asset");
break;
case "rotate":
if (!is2D) setToolMode("Rotate-Asset");
break;
case "measure":
setToolMode("MeasurementScale");
break;
case "Add pillar":
if (!is2D) setAddAction("Pillar");
break;
case "delete":
is2D ? setToolMode("2D-Delete") : setToolMode("3D-Delete");
break;
}
/>
</div>
);
};
const renderModeSwitcher = () => (
<button
id="toggle-threed-button"
className={`toggle-threed-button${toggleThreeD ? " toggled" : ""}`}
onClick={toggle2D3D}
>
<div className="tooltip">toggle view (tab)</div>
<div className={`toggle-option${!toggleThreeD ? " active" : ""}`}>2d</div>
<div className={`toggle-option${toggleThreeD ? " active" : ""}`}>3d</div>
</button>
);
const toggle2D3D = () => {
const toggleTo2D = toggleView;
setToggleView(!toggleTo2D);
setToggleThreeD(toggleTo2D);
setToggleUI(toggleTo2D, toggleTo2D);
if (toggleTo2D) {
setSelectedWallAsset(null);
setAddAction(null);
}
setActiveTool("cursor");
setActiveSubTool("cursor");
setToggleUI(
localStorage.getItem("navBarUiLeft") !== "false",
localStorage.getItem("navBarUiRight") !== "false"
);
};
const getIconByTool = (tool: string) => {
switch (tool) {
case "cursor":
return CursorIcon;
case "free-hand":
return FreeMoveIcon;
case "delete":
return DeleteIcon;
case "move":
return MoveIcon;
case "rotate":
return RotateIcon;
default:
return CursorIcon;
}
};
const getTooltipShortcut = (tool: string) => {
switch (tool) {
case "cursor":
return "v";
case "free-hand":
return "h";
case "delete":
return "x";
default:
return "";
}
};
const getIconComponent = (option: string) => {
switch (option) {
case "cursor":
return <CursorIcon isActive={false} />;
case "free-hand":
return <FreeMoveIcon isActive={false} />;
case "delete":
return <DeleteIcon isActive={false} />;
case "move":
return <MoveIcon />;
case "rotate":
return <RotateIcon />;
default:
return null;
}
};
return (
<div className={`tools-container ${showShortcuts ? "visible" : ""}`}>
<div className="activeDropicon">
{/* Tool Picker (cursor, delete, etc.) */}
{["cursor", "free-hand", "delete"].map(
(tool) =>
activeSubTool === tool && (
<ToolButton
key={tool}
toolId={tool}
icon={getIconByTool(tool)}
tooltip={`${tool} (${getTooltipShortcut(tool)})`}
active={activeTool === tool}
onClick={() => setActiveTool(tool)}
/>
)
)}
{/* Dropdown Menu */}
{activeModule !== "visualization" && (
<button
id="drop-down-button"
title="drop-down"
className="drop-down-option-button"
ref={dropdownRef}
onClick={() => setOpenDrop(!openDrop)}
>
<ArrowIcon />
{openDrop && (
<div className="drop-down-container">
{["cursor", "free-hand", "delete"].map((option) => (
<button
key={option}
id={`${option}-tool`}
className="option-list"
onClick={() => {
setActiveTool(option);
setActiveSubTool(option);
setOpenDrop(false);
}}
>
<div className="active-option">
{activeSubTool === option && <TickIcon />}
</div>
{getIconComponent(option)}
<div className="option">{option}</div>
</button>
))}
</div>
const renderBuilderTools = () => (
<>
{!toggleThreeD && (
<div className="draw-tools">
<ToolButton
toolId="drawWall"
icon={WallIcon}
tooltip="draw wall (q)"
active={activeTool === "draw-wall"}
onClick={() => setActiveTool("draw-wall")}
/>
<ToolButton
toolId="drawZone"
icon={ZoneIcon}
tooltip="draw zone (e)"
active={activeTool === "draw-zone"}
onClick={() => setActiveTool("draw-zone")}
/>
<ToolButton
toolId="drawAisle"
icon={AsileIcon}
tooltip="draw aisle (r)"
active={activeTool === "draw-aisle"}
onClick={() => setActiveTool("draw-aisle")}
/>
<ToolButton
toolId="drawFloor"
icon={FloorIcon}
tooltip="draw floor (t)"
active={activeTool === "draw-floor"}
onClick={() => setActiveTool("draw-floor")}
/>
</div>
)}
</button>
)}
</div>
{toggleThreeD && activeModule !== "visualization" && (
<>
<div className="split"></div>
<div className="transform-tools">
{["move", "rotate"].map((tool) => (
<ToolButton
key={tool}
toolId={tool}
icon={getIconByTool(tool)}
tooltip={`${tool}`}
active={activeTool === tool}
onClick={() => setActiveTool(tool)}
/>
))}
</div>
<div className="draw-tools">
<ToolButton
toolId="measureScale"
icon={MeasureToolIcon}
tooltip="measure scale (m)"
active={activeTool === "measure"}
onClick={() => setActiveTool("measure")}
/>
</div>
</>
)}
);
<div className="split"></div>
{activeModule === "builder" && renderBuilderTools()}
{activeModule === "simulation" && renderSimulationTools()}
{activeModule === "visualization" && renderVisualizationTools()}
const renderSimulationTools = () => (
<div className="draw-tools">
<ToolButton
toolId="pen"
icon={PenIcon}
tooltip="pen"
active={activeTool === "pen"}
onClick={() => setActiveTool("pen")}
/>
</div>
);
<div className="split"></div>
<div className="general-options">
<ToolButton
toolId="comment"
icon={CommentIcon}
tooltip="comment"
active={activeTool === "comment"}
onClick={() => setActiveTool("comment")}
/>
{toggleThreeD && (
<ToolButton
toolId="play"
icon={PlayIcon}
tooltip="play (ctrl + p)"
active={activeTool === "play"}
onClick={() => setIsPlaying(!isPlaying)}
/>
)}
</div>
const renderVisualizationTools = () => (
<div className="draw-tools">
<ToolButton
toolId="saveTemplate"
icon={SaveTemplateIcon}
tooltip="save template"
active={false}
onClick={() =>
handleSaveTemplate({
addTemplate,
floatingWidget,
widgets3D,
selectedZone,
templates,
visualizationSocket,
projectId,
versionId: selectedVersion?.versionId || "",
})
}
/>
</div>
);
{activeModule === "builder" && (
<>
<div className="split"></div>
{renderModeSwitcher()}
</>
)}
</div>
);
const renderModeSwitcher = () => (
<button
id="toggle-threed-button"
className={`toggle-threed-button${toggleThreeD ? " toggled" : ""}`}
onClick={toggle2D3D}
>
<div className="tooltip">toggle view (tab)</div>
<div className={`toggle-option${!toggleThreeD ? " active" : ""}`}>2d</div>
<div className={`toggle-option${toggleThreeD ? " active" : ""}`}>3d</div>
</button>
);
const getIconByTool = (tool: string) => {
switch (tool) {
case "cursor":
return CursorIcon;
case "free-hand":
return FreeMoveIcon;
case "delete":
return DeleteIcon;
case "move":
return MoveIcon;
case "rotate":
return RotateIcon;
default:
return CursorIcon;
}
};
const getTooltipShortcut = (tool: string) => {
switch (tool) {
case "cursor":
return "v";
case "free-hand":
return "h";
case "delete":
return "x";
default:
return "";
}
};
const getIconComponent = (option: string) => {
switch (option) {
case "cursor":
return <CursorIcon isActive={false} />;
case "free-hand":
return <FreeMoveIcon isActive={false} />;
case "delete":
return <DeleteIcon isActive={false} />;
case "move":
return <MoveIcon />;
case "rotate":
return <RotateIcon />;
default:
return null;
}
};
return (
<div className={`tools-container ${showShortcuts ? "visible" : ""}`}>
<div className="activeDropicon">
{/* Tool Picker (cursor, delete, etc.) */}
{["cursor", "free-hand", "delete"].map(
(tool) =>
activeSubTool === tool && (
<ToolButton
key={tool}
toolId={tool}
icon={getIconByTool(tool)}
tooltip={`${tool} (${getTooltipShortcut(tool)})`}
active={activeTool === tool}
onClick={() => setActiveTool(tool)}
/>
)
)}
{/* Dropdown Menu */}
{activeModule !== "visualization" && (
<button
id="drop-down-button"
title="drop-down"
className="drop-down-option-button"
ref={dropdownRef}
onClick={() => setOpenDrop(!openDrop)}
>
<ArrowIcon />
{openDrop && (
<div className="drop-down-container">
{["cursor", "free-hand", "delete"].map((option) => (
<button
key={option}
id={`${option}-tool`}
className="option-list"
onClick={() => {
setActiveTool(option);
setActiveSubTool(option);
setOpenDrop(false);
}}
>
<div className="active-option">
{activeSubTool === option && <TickIcon />}
</div>
{getIconComponent(option)}
<div className="option">{option}</div>
</button>
))}
</div>
)}
</button>
)}
</div>
{toggleThreeD && activeModule !== "visualization" && (
<>
<div className="split"></div>
<div className="transform-tools">
{["move", "rotate"].map((tool) => (
<ToolButton
key={tool}
toolId={tool}
icon={getIconByTool(tool)}
tooltip={`${tool}`}
active={activeTool === tool}
onClick={() => setActiveTool(tool)}
/>
))}
</div>
</>
)}
<div className="split"></div>
{activeModule === "builder" && renderBuilderTools()}
{activeModule === "simulation" && renderSimulationTools()}
{activeModule === "visualization" && renderVisualizationTools()}
<div className="split"></div>
<div className="general-options">
<ToolButton
toolId="comment"
icon={CommentIcon}
tooltip="comment"
active={activeTool === "comment"}
onClick={() => setActiveTool("comment")}
/>
{toggleThreeD && (
<ToolButton
toolId="play"
icon={PlayIcon}
tooltip="play (ctrl + p)"
active={activeTool === "play"}
onClick={() => setIsPlaying(!isPlaying)}
/>
)}
</div>
{activeModule === "builder" && (
<>
<div className="split"></div>
{renderModeSwitcher()}
</>
)}
</div>
);
};
// Extracted common store logic
const useStoreHooks = () => {
return {
...useActiveTool(),
...useToolMode(),
...useAddAction(),
};
return {
...useActiveTool(),
...useToolMode(),
...useAddAction(),
};
};
export default Tools;

View File

@@ -1,7 +1,7 @@
import * as THREE from 'three';
import { CameraControls } from '@react-three/drei';
import { ThreeEvent, useFrame, useThree } from '@react-three/fiber';
import { useEffect, useRef } from 'react';
import { useEffect, useState } from 'react';
import { useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
import useModuleStore from '../../../../store/useModuleStore';
@@ -10,6 +10,9 @@ import { useVersionContext } from '../../version/versionContext';
import { useParams } from 'react-router-dom';
import { useSceneContext } from '../../../scene/sceneContext';
import { detectModifierKeys } from '../../../../utils/shortcutkeys/detectModifierKeys';
import handleDecalPositionSnap from '../functions/handleDecalPositionSnap';
// import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi';
// import { upsertFloorApi } from '../../../../services/factoryBuilder/floor/upsertFloorApi';
@@ -34,6 +37,7 @@ export function useDecalEventHandlers({
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const { socket } = useSocketStore();
const [keyEvent, setKeyEvent] = useState<"Ctrl" | "">("");
const { raycaster, pointer, camera, scene, gl, controls } = useThree();
useFrame(() => {
@@ -51,14 +55,22 @@ export function useDecalEventHandlers({
const wallUuid = wallIntersect.object.userData.wallUuid;
const point = wallIntersect.object.worldToLocal(wallIntersect.point.clone());
let finalPos;
if (keyEvent === "Ctrl") {
finalPos = handleDecalPositionSnap(point, offset, parent, decal, 0.05)
} else {
finalPos = point
}
if ("wallUuid" in parent && parent.wallUuid === wallUuid && decal.decalType.type === 'Wall') {
updateDecalPositionInWall(decal.decalUuid, [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]]);
updateDecalPositionInWall(decal.decalUuid, [finalPos.x + offset.x, finalPos.y + offset.y, decal.decalPosition[2]]);
} else if (decal.decalType.type === 'Wall' && wallUuid) {
deleteDecal(decal.decalUuid, parent);
const addedDecal = addDecalToWall(wallUuid, {
...decal,
decalPosition: [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]],
decalPosition: [finalPos.x + offset.x, finalPos.y + offset.y, decal.decalPosition[2]],
decalType: { type: 'Wall', wallUuid: wallUuid }
});
@@ -72,7 +84,7 @@ export function useDecalEventHandlers({
const addedDecal = addDecalToWall(wallUuid, {
...decal,
decalPosition: [point.x + offset.x, point.y + offset.y, wall.wallThickness / 2 + 0.001],
decalPosition: [finalPos.x + offset.x, finalPos.y + offset.y, wall.wallThickness / 2 + 0.001],
decalType: { type: 'Wall', wallUuid: wallUuid }
});
@@ -84,14 +96,22 @@ export function useDecalEventHandlers({
const floorUuid = floorIntersect.object.userData.floorUuid;
const point = floorIntersect.object.worldToLocal(floorIntersect.point.clone());
let finalPos;
if (keyEvent === "Ctrl") {
finalPos = handleDecalPositionSnap(point, offset, parent, decal, 0.25)
} else {
finalPos = point
}
if ("floorUuid" in parent && parent.floorUuid === floorUuid && decal.decalType.type === 'Floor') {
updateDecalPositionInFloor(decal.decalUuid, [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]]);
updateDecalPositionInFloor(decal.decalUuid, [finalPos.x + offset.x, finalPos.y + offset.y, decal.decalPosition[2]]);
} else if (decal.decalType.type === 'Floor' && floorUuid) {
deleteDecal(decal.decalUuid, parent);
const addedDecal = addDecalToFloor(floorUuid, {
...decal,
decalPosition: [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]],
decalPosition: [finalPos.x + offset.x, finalPos.y + offset.y, decal.decalPosition[2]],
decalType: { type: 'Floor', floorUuid: floorUuid }
});
@@ -105,7 +125,7 @@ export function useDecalEventHandlers({
const addedDecal = addDecalToFloor(floorUuid, {
...decal,
decalPosition: [point.x + offset.x, point.y + offset.y, -0.001],
decalPosition: [finalPos.x + offset.x, finalPos.y + offset.y, -0.001],
decalType: { type: 'Floor', floorUuid: floorUuid }
});
@@ -283,18 +303,44 @@ export function useDecalEventHandlers({
const handlePointerMissed = () => {
if (selectedDecal && selectedDecal.decalMesh && selectedDecal.decalMesh.userData.decalUuid === decal.decalUuid) {
setSelectedDecal(null);
setKeyEvent("");
}
};
const onKeyUp = (event: KeyboardEvent) => {
const keyCombination = detectModifierKeys(event);
if (keyCombination === "") {
setKeyEvent("");
} else if (keyCombination === "Ctrl") {
setKeyEvent(keyCombination);
}
};
const onKeyDown = (event: KeyboardEvent) => {
const keyCombination = detectModifierKeys(event);
if (keyCombination !== keyEvent) {
if (keyCombination === "Ctrl") {
setKeyEvent(keyCombination);
} else {
setKeyEvent("");
}
}
}
useEffect(() => {
const canvasElement = gl.domElement;
if (activeModule === 'builder' && !toggleView && selectedDecal && selectedDecal.decalData.decalUuid === decal.decalUuid) {
canvasElement.addEventListener('pointerup', handlePointerUp);
canvasElement?.addEventListener("keyup", onKeyUp);
canvasElement.addEventListener("keydown", onKeyDown);
}
return () => {
canvasElement.removeEventListener('pointerup', handlePointerUp);
canvasElement?.removeEventListener("keyup", onKeyUp);
canvasElement.removeEventListener("keydown", onKeyDown);
};
}, [gl, activeModule, toggleView, selectedDecal, camera, controls, visible, parent, decal, decalDragState]);

View File

@@ -0,0 +1,43 @@
import * as THREE from 'three';
function snapToFixedPoint(
position: [number, number, number],
snapInterval: number = 0.1
): [number, number, number] {
return [
Math.round(position[0] / snapInterval) * snapInterval,
Math.round(position[1] / snapInterval) * snapInterval,
Math.round(position[2] / snapInterval) * snapInterval,
];
}
function handleDecalPositionSnap(
point: THREE.Vector3,
offset: THREE.Vector3,
parent: Wall | Floor,
decal: Decal,
snapInterval: number = 0.1
): THREE.Vector3 {
let rawPos: [number, number, number];
if ("wallUuid" in parent) {
// snap relative to wall
rawPos = [
point.x + offset.x,
point.y + offset.y,
decal.decalPosition[2], // keep depth as-is
];
} else {
// snap relative to floor
rawPos = [
point.x + offset.x,
point.y + offset.y,
decal.decalPosition[2],
];
}
const snapped = snapToFixedPoint(rawPos, snapInterval);
return new THREE.Vector3(snapped[0], snapped[1], snapped[2]);
}
export default handleDecalPositionSnap;

View File

@@ -1,7 +1,7 @@
import * as THREE from "three"
import { useEffect } from 'react'
import { getFloorAssets } from '../../../services/factoryBuilder/asset/floorAsset/getFloorItemsApi';
import { useLoadingProgress, useRenameModeStore, useSelectedFloorItem, useSelectedItem, useSocketStore } from '../../../store/builder/store';
import { useLoadingProgress, useRenameModeStore, useSelectedItem, useSocketStore } from '../../../store/builder/store';
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { FloorItems, RefMesh } from "../../../types/world/worldTypes";
@@ -15,6 +15,7 @@ import { useLeftData, useTopData } from "../../../store/visualization/useZone3DW
import { getUserData } from "../../../functions/getUserData";
import { useSceneContext } from "../../scene/sceneContext";
import { useVersionContext } from "../version/versionContext";
import { useBuilderStore } from "../../../store/builder/useBuilderStore";
const gltfLoaderWorker = new Worker(new URL("../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js", import.meta.url));
@@ -28,7 +29,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
const { selectedVersion } = selectedVersionStore();
const { setAssets, addAsset, clearAssets } = assetStore();
const { addEvent, clearEvents } = eventStore();
const { setSelectedFloorItem } = useSelectedFloorItem();
const { setSelectedFloorAsset } = useBuilderStore();
const { selectedItem, setSelectedItem } = useSelectedItem();
const { projectId } = useParams();
const { isRenameMode, setIsRenameMode } = useRenameModeStore();
@@ -377,7 +378,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
if ((controls as CameraControls)) {
const target = (controls as CameraControls).getTarget(new THREE.Vector3());
(controls as CameraControls).setTarget(target.x, 0, target.z, true);
setSelectedFloorItem(null);
setSelectedFloorAsset(null);
}
}

View File

@@ -3,7 +3,7 @@ import { CameraControls } from '@react-three/drei';
import { ThreeEvent, useThree } from '@react-three/fiber';
import { useCallback, useEffect, useRef } from 'react';
import { useActiveTool, useDeletableFloorItem, useResourceManagementId, useSelectedFloorItem, useToggleView, useZoneAssetId } from '../../../../../../store/builder/store';
import { useActiveTool, useResourceManagementId, useToggleView, useZoneAssetId } from '../../../../../../store/builder/store';
import useModuleStore, { useSubModuleStore } from '../../../../../../store/useModuleStore';
import { useSocketStore } from '../../../../../../store/builder/store';
import { useSceneContext } from '../../../../../scene/sceneContext';
@@ -13,8 +13,9 @@ import { useParams } from 'react-router-dom';
import { getUserData } from '../../../../../../functions/getUserData';
import { useLeftData, useTopData } from '../../../../../../store/visualization/useZone3DWidgetStore';
import { useSelectedAsset } from '../../../../../../store/simulation/useSimulationStore';
import { upsertProductOrEventApi } from '../../../../../../services/simulation/products/UpsertProductOrEventApi';
import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore';
import { upsertProductOrEventApi } from '../../../../../../services/simulation/products/UpsertProductOrEventApi';
// import { deleteFloorItem } from '../../../../../../services/factoryBuilder/asset/floorAsset/deleteFloorItemApi';
export function useModelEventHandlers({
@@ -40,8 +41,7 @@ export function useModelEventHandlers({
const { removeEvent, getEventByModelUuid } = eventStore();
const { getIsEventInProduct, addPoint, deleteEvent } = productStore();
const { setSelectedAsset, clearSelectedAsset } = useSelectedAsset();
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { deletableFloorAsset, setDeletableFloorAsset, selectedFloorAsset, setSelectedFloorAsset } = useBuilderStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { selectedVersionStore } = useVersionContext();
@@ -89,10 +89,10 @@ export function useModelEventHandlers({
}, [resourceManagementId])
useEffect(() => {
if (!selectedFloorItem) {
if (!selectedFloorAsset) {
setZoneAssetId(null);
}
}, [selectedFloorItem])
}, [selectedFloorAsset])
const handleDblClick = (asset: Asset) => {
if (asset && activeTool === "cursor" && boundingBox && groupRef.current && (activeModule === 'builder' || (activeModule === 'simulation' && resourceManagementId))) {
@@ -137,14 +137,14 @@ export function useModelEventHandlers({
(controls as CameraControls).setLookAt(newCameraPos.x, newCameraPos.y, newCameraPos.z, collisionPos.x, 0, collisionPos.z, true);
}
setSelectedFloorItem(groupRef.current);
setSelectedFloorAsset(groupRef.current);
setResourceManagementId("");
}
};
const handleClick = async (evt: ThreeEvent<MouseEvent>, asset: Asset) => {
if (leftDrag.current || toggleView) return;
if (activeTool === 'delete' && deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) {
if (activeTool === 'delete' && deletableFloorAsset && deletableFloorAsset.uuid === asset.modelUuid) {
//REST
@@ -236,19 +236,19 @@ export function useModelEventHandlers({
const handlePointerOver = useCallback((asset: Asset) => {
if (activeTool === "delete" && activeModule === 'builder') {
if (deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) {
if (deletableFloorAsset && deletableFloorAsset.uuid === asset.modelUuid) {
return;
} else {
setDeletableFloorItem(groupRef.current);
setDeletableFloorAsset(groupRef.current);
}
}
}, [activeTool, activeModule, deletableFloorItem]);
}, [activeTool, activeModule, deletableFloorAsset]);
const handlePointerOut = useCallback((evt: ThreeEvent<MouseEvent>, asset: Asset) => {
if (evt.intersections.length === 0 && activeTool === "delete" && deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) {
setDeletableFloorItem(null);
if (evt.intersections.length === 0 && activeTool === "delete" && deletableFloorAsset && deletableFloorAsset.uuid === asset.modelUuid) {
setDeletableFloorAsset(null);
}
}, [activeTool, deletableFloorItem]);
}, [activeTool, deletableFloorAsset]);
const handleContextMenu = (asset: Asset, evt: ThreeEvent<MouseEvent>) => {
if (rightDrag.current || toggleView) return;

View File

@@ -2,8 +2,9 @@ import * as THREE from 'three';
import { useEffect, useRef, useState } from 'react';
import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils';
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { useDeletableFloorItem, useSelectedAssets, useSelectedFloorItem, useToggleView, useToolMode } from '../../../../../store/builder/store';
import { useSelectedAssets, useToggleView, useToolMode } from '../../../../../store/builder/store';
import { AssetBoundingBox } from '../../functions/assetBoundingBox';
import { useBuilderStore } from '../../../../../store/builder/useBuilderStore';
import useModuleStore from '../../../../../store/useModuleStore';
import { useSceneContext } from '../../../../scene/sceneContext';
import { SkeletonUtils } from 'three-stdlib';
@@ -20,8 +21,7 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere
const { activeModule } = useModuleStore();
const { assetStore } = useSceneContext();
const { resetAnimation } = assetStore();
const { setDeletableFloorItem } = useDeletableFloorItem();
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { setDeletableFloorAsset, selectedFloorAsset, setSelectedFloorAsset } = useBuilderStore();
const [gltfScene, setGltfScene] = useState<GLTF["scene"] | null>(null);
const [boundingBox, setBoundingBox] = useState<THREE.Box3 | null>(null);
const [isSelected, setIsSelected] = useState(false);
@@ -53,17 +53,17 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere
}, [asset.modelUuid, fieldData])
useEffect(() => {
setDeletableFloorItem(null);
if (selectedFloorItem === null || selectedFloorItem.userData.modelUuid !== asset.modelUuid) {
setDeletableFloorAsset(null);
if (selectedFloorAsset === null || selectedFloorAsset.userData.modelUuid !== asset.modelUuid) {
resetAnimation(asset.modelUuid);
}
}, [activeModule, toolMode, selectedFloorItem])
}, [activeModule, toolMode, selectedFloorAsset])
useEffect(() => {
if (selectedFloorItem && selectedFloorItem.userData.modelUuid === asset.modelUuid) {
setSelectedFloorItem(groupRef.current);
if (selectedFloorAsset && selectedFloorAsset.userData.modelUuid === asset.modelUuid) {
setSelectedFloorAsset(groupRef.current);
}
}, [isRendered, selectedFloorItem])
}, [isRendered, selectedFloorAsset])
useEffect(() => {
if (selectedAssets.length > 0) {

View File

@@ -2,9 +2,10 @@ import { useEffect, useRef, useState } from "react";
import { useThree, useFrame } from "@react-three/fiber";
import { Group, Vector3 } from "three";
import { CameraControls } from '@react-three/drei';
import { useLimitDistance, useRenderDistance, useSelectedFloorItem, useToggleView } from '../../../../store/builder/store';
import { useLimitDistance, useRenderDistance } from '../../../../store/builder/store';
import { useSelectedAsset } from '../../../../store/simulation/useSimulationStore';
import { useSceneContext } from '../../../scene/sceneContext';
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
import Model from './model/model';
import { GLTFLoader } from "three/examples/jsm/Addons";
@@ -16,7 +17,7 @@ function Models({ loader }: { loader: GLTFLoader }) {
const assetGroupRef = useRef<Group>(null);
const { assetStore } = useSceneContext();
const { assets } = assetStore();
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { selectedFloorAsset, setSelectedFloorAsset } = useBuilderStore();
const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
const { limitDistance } = useLimitDistance();
const { renderDistance } = useRenderDistance();
@@ -49,10 +50,10 @@ function Models({ loader }: { loader: GLTFLoader }) {
ref={assetGroupRef}
onPointerMissed={(e) => {
e.stopPropagation();
if (selectedFloorItem) {
if (selectedFloorAsset) {
const target = (controls as CameraControls).getTarget(new Vector3());
(controls as CameraControls).setTarget(target.x, 0, target.z, true);
setSelectedFloorItem(null);
setSelectedFloorAsset(null);
}
if (selectedAsset) {
clearSelectedAsset();

View File

@@ -17,6 +17,7 @@ import CopyPasteControls3D from "./copyPasteControls3D";
import MoveControls3D from "./moveControls3D";
import RotateControls3D from "./rotateControls3D";
import TransformControls3D from "./transformControls3D";
import { useBuilderStore } from "../../../../../store/builder/useBuilderStore";
// import { deleteFloorItem } from '../../../../../services/factoryBuilder/asset/floorAsset/deleteFloorItemApi';
@@ -30,6 +31,7 @@ const SelectionControls3D: React.FC = () => {
const { socket } = useSocketStore();
const { contextAction, setContextAction } = useContextActionStore()
const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext();
const { selectedDecal, selectedWall, selectedAisle, selectedFloor, selectedFloorAsset, selectedWallAsset } = useBuilderStore();
const { push3D } = undoRedo3DStore();
const { removeAsset, getAssetById, movedObjects, rotatedObjects, copiedObjects, pastedObjects, duplicatedObjects, setPastedObjects, setDuplicatedObjects } = assetStore();
const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]);
@@ -203,7 +205,7 @@ const SelectionControls3D: React.FC = () => {
rightClickMoved.current = false;
};
if (!toggleView && activeModule === "builder" && (toolMode === 'cursor' || toolMode === 'Move-Asset' || toolMode === 'Rotate-Asset')) {
if (!toggleView && activeModule === "builder" && (toolMode === 'cursor' || toolMode === 'Move-Asset' || toolMode === 'Rotate-Asset') && (!selectedDecal && !selectedWall && !selectedAisle && !selectedFloor && !selectedFloorAsset && !selectedWallAsset) && duplicatedObjects.length === 0 && pastedObjects.length === 0) {
helper.enabled = true;
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
@@ -225,7 +227,7 @@ const SelectionControls3D: React.FC = () => {
helper.dispose();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, rotatedObjects, activeModule, toolMode]);
}, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, rotatedObjects, activeModule, toolMode, selectedDecal, selectedWall, selectedAisle, selectedFloor, selectedFloorAsset, selectedWallAsset]);
useEffect(() => {
if (activeModule !== "builder" || (toolMode !== 'cursor' && toolMode !== 'Move-Asset' && toolMode !== 'Rotate-Asset') || toggleView) {

View File

@@ -7,7 +7,8 @@ import { useProductContext } from "../../../simulation/products/productContext";
import { getUserData } from "../../../../functions/getUserData";
import { useSceneContext } from "../../sceneContext";
import { useVersionContext } from "../../../builder/version/versionContext";
import { useSelectedFloorItem, useObjectPosition, useObjectRotation, useActiveTool, useSocketStore } from "../../../../store/builder/store";
import { useObjectPosition, useObjectRotation, useActiveTool, useSocketStore } from "../../../../store/builder/store";
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
@@ -17,7 +18,7 @@ export default function TransformControl() {
const state = useThree();
const ref = useRef(null);
const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null);
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { selectedFloorAsset, setSelectedFloorAsset } = useBuilderStore();
const { setObjectPosition } = useObjectPosition();
const { setObjectRotation } = useObjectRotation();
const { activeTool } = useActiveTool();
@@ -48,26 +49,27 @@ export default function TransformControl() {
};
function handleObjectChange() {
if (selectedFloorItem) {
setObjectPosition(selectedFloorItem.position);
if (selectedFloorAsset) {
setObjectPosition(selectedFloorAsset.position);
setObjectRotation({
x: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.x),
y: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.y),
z: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.z),
x: THREE.MathUtils.radToDeg(selectedFloorAsset.rotation.x),
y: THREE.MathUtils.radToDeg(selectedFloorAsset.rotation.y),
z: THREE.MathUtils.radToDeg(selectedFloorAsset.rotation.z),
});
}
}
function handleMouseUp() {
if (selectedFloorItem) {
setObjectPosition(selectedFloorItem.position);
if (!selectedProduct || !selectedFloorAsset) return;
if (selectedFloorAsset) {
setObjectPosition(selectedFloorAsset.position);
setObjectRotation({
x: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.x),
y: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.y),
z: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.z),
x: THREE.MathUtils.radToDeg(selectedFloorAsset.rotation.x),
y: THREE.MathUtils.radToDeg(selectedFloorAsset.rotation.y),
z: THREE.MathUtils.radToDeg(selectedFloorAsset.rotation.z),
});
}
const asset = getAssetById(selectedFloorItem?.uuid);
const asset = getAssetById(selectedFloorAsset.uuid);
if (asset) {
if (asset.eventData) {
const eventData = eventStore.getState().getEventByModelUuid(asset.modelUuid);
@@ -75,8 +77,8 @@ export default function TransformControl() {
if (eventData) {
eventStore.getState().updateEvent(asset.modelUuid, {
position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z] as [number, number, number],
rotation: [selectedFloorItem.rotation.x, selectedFloorItem.rotation.y, selectedFloorItem.rotation.z] as [number, number, number],
position: [selectedFloorAsset.position.x, 0, selectedFloorAsset.position.z] as [number, number, number],
rotation: [selectedFloorAsset.rotation.x, selectedFloorAsset.rotation.y, selectedFloorAsset.rotation.z] as [number, number, number],
});
}
@@ -87,8 +89,8 @@ export default function TransformControl() {
selectedProduct.productUuid,
asset.modelUuid,
{
position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z] as [number, number, number],
rotation: [selectedFloorItem.rotation.x, selectedFloorItem.rotation.y, selectedFloorItem.rotation.z] as [number, number, number],
position: [selectedFloorAsset.position.x, 0, selectedFloorAsset.position.z] as [number, number, number],
rotation: [selectedFloorAsset.rotation.x, selectedFloorAsset.rotation.y, selectedFloorAsset.rotation.z] as [number, number, number],
}
);
@@ -104,8 +106,8 @@ export default function TransformControl() {
}
updateAsset(asset.modelUuid, {
position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z],
rotation: [selectedFloorItem.rotation.x, selectedFloorItem.rotation.y, selectedFloorItem.rotation.z] as [number, number, number],
position: [selectedFloorAsset.position.x, 0, selectedFloorAsset.position.z],
rotation: [selectedFloorAsset.rotation.x, selectedFloorAsset.rotation.y, selectedFloorAsset.rotation.z] as [number, number, number],
});
//REST
@@ -114,8 +116,8 @@ export default function TransformControl() {
// organization,
// asset.modelUuid,
// asset.modelName,
// [selectedFloorItem.position.x, 0, selectedFloorItem.position.z,
// { "x": selectedFloorItem.rotation.x, "y": selectedFloorItem.rotation.y, "z": selectedFloorItem.rotation.z },
// [selectedFloorAsset.position.x, 0, selectedFloorAsset.position.z,
// { "x": selectedFloorAsset.rotation.x, "y": selectedFloorAsset.rotation.y, "z": selectedFloorAsset.rotation.z },
// asset.assetId,
// false,
// true,
@@ -128,8 +130,8 @@ export default function TransformControl() {
modelUuid: asset.modelUuid,
modelName: asset.modelName,
assetId: asset.assetId,
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 },
position: [selectedFloorAsset.position.x, 0, selectedFloorAsset.position.z] as [number, number, number],
rotation: { x: selectedFloorAsset.rotation.x, y: selectedFloorAsset.rotation.y, z: selectedFloorAsset.rotation.z },
isLocked: false,
isVisible: true,
socketId: socket.id,
@@ -151,8 +153,8 @@ export default function TransformControl() {
assetData: asset,
newData: {
...asset,
position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z],
rotation: [selectedFloorItem.rotation.x, selectedFloorItem.rotation.y, selectedFloorItem.rotation.z],
position: [selectedFloorAsset.position.x, 0, selectedFloorAsset.position.z],
rotation: [selectedFloorAsset.rotation.x, selectedFloorAsset.rotation.y, selectedFloorAsset.rotation.z],
},
timeStap: new Date().toISOString()
}
@@ -170,7 +172,7 @@ export default function TransformControl() {
element?.getAttribute("contenteditable") === "true";
if (isTextInput(document.activeElement)) return;
const keyCombination = detectModifierKeys(e);
if (!selectedFloorItem) return;
if (!selectedFloorAsset) return;
if (keyCombination === "G") {
setTransformMode((prev) => (prev === "translate" ? null : "translate"));
}
@@ -179,13 +181,13 @@ export default function TransformControl() {
}
};
if (selectedFloorItem) {
if (selectedFloorAsset) {
window.addEventListener("keydown", handleKeyDown);
setObjectPosition(selectedFloorItem.position);
setObjectPosition(selectedFloorAsset.position);
setObjectRotation({
x: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.x),
y: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.y),
z: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.z),
x: THREE.MathUtils.radToDeg(selectedFloorAsset.rotation.x),
y: THREE.MathUtils.radToDeg(selectedFloorAsset.rotation.y),
z: THREE.MathUtils.radToDeg(selectedFloorAsset.rotation.z),
});
} else {
setTransformMode(null);
@@ -194,7 +196,7 @@ export default function TransformControl() {
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, [selectedFloorItem]);
}, [selectedFloorAsset]);
useEffect(() => {
if (activeTool === "delete") {
@@ -202,7 +204,7 @@ export default function TransformControl() {
const target = (state.controls as any).getTarget(new THREE.Vector3());
(state.controls as any).setTarget(target.x, 0, target.z, true);
}
setSelectedFloorItem(null);
setSelectedFloorAsset(null);
setObjectPosition({ x: undefined, y: undefined, z: undefined });
setObjectRotation({ x: undefined, y: undefined, z: undefined });
}
@@ -210,13 +212,13 @@ export default function TransformControl() {
return (
<>
{(selectedFloorItem && transformMode) &&
{(selectedFloorAsset && transformMode) &&
<TransformControls
ref={ref}
showX={transformMode === "translate"}
showY={transformMode === "rotate" || transformMode === "translate"}
showZ={transformMode === "translate"}
object={selectedFloorItem}
object={selectedFloorAsset}
mode={transformMode}
onObjectChange={handleObjectChange}
onMouseUp={handleMouseUp}

View File

@@ -0,0 +1,41 @@
import { Outline } from "@react-three/postprocessing";
import { BlendFunction } from "postprocessing";
import { Object3D } from "three";
type OutlineInstanceProps = {
selection?: Object3D | Object3D[] | null;
color: number;
xRay?: boolean;
width?: number;
edgeStrength?: number;
};
function OutlineInstance({
selection,
color,
xRay = true,
width = 2000,
edgeStrength = 5,
}: OutlineInstanceProps) {
if (!selection) return null;
const sel = Array.isArray(selection) ? selection : [selection];
return (
<Outline
selection={sel}
selectionLayer={10}
width={width}
blendFunction={BlendFunction.ALPHA}
edgeStrength={edgeStrength}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={color}
hiddenEdgeColor={color}
blur
xRay={xRay}
/>
);
}
export default OutlineInstance

View File

@@ -0,0 +1,89 @@
import { Object3D } from "three";
import { useDeletableEventSphere, useSelectedEventSphere } from "../../../../store/simulation/useSimulationStore";
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
import * as CONSTANTS from "../../../../types/world/worldConstants";
import OutlineInstance from "./outlineInstance/outlineInstance";
function flattenChildren(children: Object3D[]): Object3D[] {
return children.flatMap((child) => [child, ...flattenChildren(child.children)]);
}
function OutlineInstances() {
const { selectedEventSphere } = useSelectedEventSphere();
const { deletableEventSphere } = useDeletableEventSphere();
const { selectedAisle, selectedWall, selectedDecal, selectedFloor, selectedWallAsset, deletableWallAsset, selectedFloorAsset, deletableFloorAsset, deletableDecal } = useBuilderStore();
return (
<>
<OutlineInstance
key="selectedWallAsset"
selection={selectedWallAsset && flattenChildren(selectedWallAsset.children)}
color={CONSTANTS.outlineConfig.assetSelectColor}
/>
<OutlineInstance
key="selectedFloorAsset"
selection={selectedFloorAsset && flattenChildren(selectedFloorAsset.children)}
color={CONSTANTS.outlineConfig.assetSelectColor}
/>
<OutlineInstance
key="deletableWallAsset"
selection={deletableWallAsset && flattenChildren(deletableWallAsset.children)}
color={CONSTANTS.outlineConfig.assetDeleteColor}
/>
<OutlineInstance
key="deletableFloorAsset"
selection={deletableFloorAsset && flattenChildren(deletableFloorAsset.children)}
color={CONSTANTS.outlineConfig.assetDeleteColor}
/>
{/* Aisle / Wall / Floor */}
<OutlineInstance
key="selectedAisle"
selection={selectedAisle?.aisleMesh && flattenChildren(selectedAisle.aisleMesh.children)}
color={CONSTANTS.outlineConfig.assetSelectColor}
xRay={false}
/>
<OutlineInstance
key="selectedWall"
selection={selectedWall ? [selectedWall] : null}
color={CONSTANTS.outlineConfig.assetSelectColor}
/>
<OutlineInstance
key="selectedFloor"
selection={selectedFloor ? [selectedFloor] : null}
color={CONSTANTS.outlineConfig.assetSelectColor}
/>
{/* Decals */}
<OutlineInstance
key="selectedDecal"
selection={selectedDecal?.decalMesh ? [selectedDecal.decalMesh] : null}
color={CONSTANTS.outlineConfig.assetSelectColor}
/>
<OutlineInstance
key="deletableDecal"
selection={deletableDecal ? [deletableDecal] : null}
color={CONSTANTS.outlineConfig.assetDeleteColor}
width={3000}
/>
{/* Event Spheres */}
<OutlineInstance
key="selectedEventSphere"
selection={selectedEventSphere ? [selectedEventSphere] : null}
color={0x6f42c1}
edgeStrength={10}
width={1000}
/>
<OutlineInstance
key="deletableEventSphere"
selection={deletableEventSphere ? [deletableEventSphere] : null}
color={CONSTANTS.outlineConfig.assetDeleteColor}
edgeStrength={10}
width={1000}
/>
</>
)
}
export default OutlineInstances;

View File

@@ -1,273 +1,22 @@
import { useEffect } from "react";
import { BlendFunction } from "postprocessing";
import { DepthOfField, Bloom, EffectComposer, N8AO, Outline } from "@react-three/postprocessing";
import { useDeletableFloorItem, useSelectedWallItem, useSelectedFloorItem, } from "../../../store/builder/store";
import { useDeletableEventSphere, useSelectedEventSphere, useSelectedPoints } from "../../../store/simulation/useSimulationStore";
import { useBuilderStore } from "../../../store/builder/useBuilderStore";
import * as CONSTANTS from "../../../types/world/worldConstants";
import { DepthOfField, Bloom, EffectComposer, N8AO } from "@react-three/postprocessing";
import OutlineInstances from "./outlineInstances/outlineInstances";
import { useDeletableEventSphere, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore";
export default function PostProcessing() {
const { selectedPoints } = useSelectedPoints();
const { deletableFloorItem } = useDeletableFloorItem();
const { selectedWallItem } = useSelectedWallItem();
const { selectedFloorItem } = useSelectedFloorItem();
const { selectedEventSphere } = useSelectedEventSphere();
const { deletableEventSphere } = useDeletableEventSphere();
const { selectedAisle, selectedWall, selectedDecal, selectedFloor, selectedWallAsset, deletableWallAsset, deletableDecal } = useBuilderStore();
function flattenChildren(children: any[]) {
const allChildren: any[] = [];
children.forEach((child) => {
allChildren.push(child);
if (child.children && child.children.length > 0) {
allChildren.push(...flattenChildren(child.children));
}
});
return allChildren;
}
useEffect(() => {
// console.log('selectedFloorItem: ', selectedFloorItem);
}, [selectedFloorItem])
useEffect(() => {
// console.log('selectedFloorItem: ', deletableFloorItem);
}, [deletableFloorItem])
useEffect(() => {
// console.log('selectedAisle: ', selectedAisle);
}, [selectedAisle])
useEffect(() => {
// console.log('selectedWall: ', selectedWall);
}, [selectedWall])
useEffect(() => {
// console.log('selectedFloor: ', selectedFloor);
}, [selectedFloor])
useEffect(() => {
// console.log('selectedWallAsset: ', selectedWallAsset);
}, [selectedWallAsset])
useEffect(() => {
// console.log('deletableWallAsset: ', deletableWallAsset);
}, [deletableWallAsset])
useEffect(() => {
// console.log('deletableEventSphere: ', deletableEventSphere);
}, [deletableEventSphere])
useEffect(() => {
// console.log('selectedPoints: ', selectedPoints);
}, [selectedPoints])
useEffect(() => {
// console.log('deletableDecal: ', deletableDecal);
}, [deletableDecal])
const { } = useSelectedEventSphere();
const { } = useDeletableEventSphere();
return (
<EffectComposer autoClear={false}>
<N8AO
color="black"
aoRadius={20}
intensity={7}
distanceFalloff={4}
aoSamples={32}
denoiseRadius={6}
denoiseSamples={16}
/>
{/* <DepthOfField
focusDistance={0}
focalLength={0.15}
bokehScale={2}
/> */}
{/* <Bloom
intensity={0.1}
luminanceThreshold={0.9}
luminanceSmoothing={0.025}
mipmapBlur={false}
/> */}
{selectedWallAsset && (
<Outline
selection={flattenChildren(selectedWallAsset.children)}
selectionLayer={10}
width={2000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={5}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
blur={true}
xRay={true}
/>
)}
{deletableWallAsset && (
<Outline
selection={flattenChildren(deletableWallAsset.children)}
selectionLayer={10}
width={2000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={5}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor}
hiddenEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor}
blur={true}
xRay={true}
/>
)}
{selectedAisle && (
<Outline
selection={flattenChildren(selectedAisle.aisleMesh?.children || [])}
selectionLayer={10}
width={2000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={5}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
blur={true}
xRay={false}
/>
)}
{selectedWall && (
<Outline
selection={selectedWall}
selectionLayer={10}
width={2000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={5}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
blur={true}
xRay={true}
/>
)}
{selectedFloor && (
<Outline
selection={selectedFloor}
selectionLayer={10}
width={2000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={5}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
blur={true}
xRay={true}
/>
)}
{selectedDecal && (
<Outline
selection={selectedDecal.decalMesh || undefined}
selectionLayer={10}
width={2000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={5}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
blur={true}
xRay={true}
/>
)}
{deletableDecal && (
<Outline
selection={deletableDecal}
selectionLayer={10}
width={3000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={5}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor}
hiddenEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor}
blur={true}
xRay={true}
/>
)}
{deletableFloorItem && (
<Outline
selection={flattenChildren(deletableFloorItem.children)}
selectionLayer={10}
width={3000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={5}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor}
hiddenEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor}
blur={true}
xRay={true}
/>
)}
{selectedWallItem && (
<Outline
selection={flattenChildren(selectedWallItem.children)}
selectionLayer={10}
width={3000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={5}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
blur={true}
xRay={true}
/>
)}
{selectedFloorItem && (
<Outline
selection={flattenChildren(selectedFloorItem.children)}
selectionLayer={10}
width={3000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={5}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
blur={true}
xRay={true}
/>
)}
{selectedEventSphere && (
<Outline
selection={[selectedEventSphere]}
selectionLayer={10}
width={1000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={10}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={0x6f42c1}
hiddenEdgeColor={0x6f42c1}
blur={true}
xRay={true}
/>
)}
{deletableEventSphere && (
<Outline
selection={[deletableEventSphere]}
selectionLayer={10}
width={1000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={10}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor}
hiddenEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor}
blur={true}
xRay={true}
/>
)}
<N8AO color="black" aoRadius={20} intensity={7} distanceFalloff={4} aoSamples={32} denoiseRadius={6} denoiseSamples={16} />
{/* <DepthOfField focusDistance={0} focalLength={0.15} bokehScale={2} /> */}
{/* <Bloom intensity={0.1} luminanceThreshold={0.9} luminanceSmoothing={0.025} mipmapBlur={false} /> */}
<OutlineInstances />
</EffectComposer>
);
}
}

View File

@@ -73,61 +73,6 @@ export const useSocketStore = create<any>((set: any, get: any) => ({
});
},
}));
// export const useSocketStore = create<any>((set: any, get: any) => ({
// socket: null,
// initializeSocket: (
// email: string,
// organization: string,
// userId?: string,
// token?: string
// ) => {
// const existingSocket = get().socket;
// if (existingSocket) {
// return;
// }
// const socket = io(
// `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder`,
// {
// reconnection: true,
// auth: { email, organization },
// }
// );
// const visualizationSocket = io(
// `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Visualization`,
// {
// reconnection: true,
// auth: { email, organization },
// }
// );
// const dashBoardSocket = io(
// `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/dashboard`,
// {
// reconnection: true,
// auth: { token },
// }
// );
// // const dashBoardSocket = io(
// // `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/project`,
// // {
// // reconnection: true,
// // auth: { token },
// // }
// // );
// set({ socket, visualizationSocket, dashBoardSocket });
// },
// disconnectSocket: () => {
// set((state: any) => {
// state.socket?.disconnect();
// state.visualizationSocket?.disconnect();
// state.dashBoardSocket?.disconnect();
// return { socket: null };
// });
// },
// }));
export const useLoadingProgress = create<{
loadingProgress: number;
@@ -213,21 +158,6 @@ export const useToolMode = create<any>((set: any) => ({
setToolMode: (x: any) => set(() => ({ toolMode: x })),
}));
export const useSelectedWallItem = create<any>((set: any) => ({
selectedWallItem: null,
setSelectedWallItem: (x: any) => set(() => ({ selectedWallItem: x })),
}));
export const useSelectedFloorItem = create<any>((set: any) => ({
selectedFloorItem: null,
setSelectedFloorItem: (x: any) => set(() => ({ selectedFloorItem: x })),
}));
export const useDeletableFloorItem = create<any>((set: any) => ({
deletableFloorItem: null,
setDeletableFloorItem: (x: any) => set(() => ({ deletableFloorItem: x })),
}));
export const useSetScale = create<any>((set: any) => ({
scale: null,
setScale: (x: any) => set(() => ({ scale: x })),

View File

@@ -15,6 +15,7 @@ interface BuilderState {
// Floor Asset
selectedFloorAsset: Object3D | null;
deletableFloorAsset: Object3D | null;
loopAnimation: boolean;
// Wall Settings
@@ -47,7 +48,7 @@ interface BuilderState {
},
// Aisle General
selectedAisle: {aisleMesh: Object3D | null, aisleData: Aisle} | null;
selectedAisle: { aisleMesh: Object3D | null, aisleData: Aisle } | null;
aisleType: AisleTypes;
aisleWidth: number;
aisleColor: AisleColors;
@@ -71,6 +72,7 @@ interface BuilderState {
// Setters - Floor Asset
setSelectedFloorAsset: (asset: Object3D | null) => void;
setDeletableFloorAsset: (asset: Object3D | null) => void;
setLoopAnimation: (loop: boolean) => void;
// Setters - Wall
@@ -97,7 +99,7 @@ interface BuilderState {
setDecalDragState: (isDragging: boolean, draggingDecalUuid: string | null, dragOffset: Vector3 | null) => void;
// Setters - Aisle General
setSelectedAisle: (aisle: {aisleMesh: Object3D | null, aisleData: Aisle} | null) => void;
setSelectedAisle: (aisle: { aisleMesh: Object3D | null, aisleData: Aisle } | null) => void;
setAisleType: (type: AisleTypes) => void;
setAisleWidth: (width: number) => void;
setAisleColor: (color: AisleColors) => void;
@@ -128,6 +130,7 @@ export const useBuilderStore = create<BuilderState>()(
deletableWallAsset: null,
selectedFloorAsset: null,
deletableFloorAsset: null,
loopAnimation: true,
selectedWall: null,
@@ -214,6 +217,12 @@ export const useBuilderStore = create<BuilderState>()(
});
},
setDeletableFloorAsset(asset: Object3D | null) {
set((state) => {
state.deletableFloorAsset = asset;
});
},
setLoopAnimation(loopAnimation: boolean) {
set((state) => {
state.loopAnimation = loopAnimation;
@@ -325,7 +334,7 @@ export const useBuilderStore = create<BuilderState>()(
// === Setters: Aisle General ===
setSelectedAisle: (aisle: {aisleMesh: Object3D | null, aisleData: Aisle} | null) => {
setSelectedAisle: (aisle: { aisleMesh: Object3D | null, aisleData: Aisle } | null) => {
set((state) => {
state.selectedAisle = aisle;
});

View File

@@ -1,293 +1,252 @@
import React, { useEffect } from "react";
import useModuleStore, {
useSubModuleStore,
useThreeDStore,
} from "../../store/useModuleStore";
import useModuleStore, { useSubModuleStore, useThreeDStore } from "../../store/useModuleStore";
import { usePlayerStore, useToggleStore } from "../../store/useUIToggleStore";
import useVersionHistoryVisibleStore, {
useActiveSubTool,
useActiveTool,
useAddAction,
useDfxUpload,
useRenameModeStore,
useSaveVersion,
useSelectedAssets,
useSelectedComment,
useSelectedFloorItem,
useSelectedWallItem,
useShortcutStore,
useToggleView,
useToolMode,
useViewSceneStore,
} from "../../store/builder/store";
import useCameraModeStore, {
usePlayButtonStore,
} from "../../store/usePlayButtonStore";
import useVersionHistoryVisibleStore, { useActiveSubTool, useActiveTool, useAddAction, useDfxUpload, useRenameModeStore, useSaveVersion, useSelectedAssets, useSelectedComment, useShortcutStore, useToggleView, useToolMode, useViewSceneStore } from "../../store/builder/store";
import useCameraModeStore, { usePlayButtonStore } from "../../store/usePlayButtonStore";
import { detectModifierKeys } from "./detectModifierKeys";
import { useSelectedZoneStore } from "../../store/visualization/useZoneStore";
import { useLogger } from "../../components/ui/log/LoggerContext";
import { useComparisonProduct } from "../../store/simulation/useSimulationStore";
import { useVersionHistoryStore } from "../../store/builder/useVersionHistoryStore";
import { useBuilderStore } from "../../store/builder/useBuilderStore";
const KeyPressListener: React.FC = () => {
const { comparisonProduct, clearComparisonProduct } = useComparisonProduct();
const { activeModule, setActiveModule } = useModuleStore();
const { setSubModule } = useSubModuleStore();
const { setActiveSubTool } = useActiveSubTool();
const { toggleUILeft, toggleUIRight, setToggleUI } = useToggleStore();
const { setToggleThreeD } = useThreeDStore();
const { setToolMode } = useToolMode();
const { isPlaying, setIsPlaying } = usePlayButtonStore();
const { toggleView, setToggleView } = useToggleView();
const { setAddAction } = useAddAction();
const { setSelectedWallItem } = useSelectedWallItem();
const { setActiveTool } = useActiveTool();
const { clearSelectedZone } = useSelectedZoneStore();
const { showShortcuts, setShowShortcuts } = useShortcutStore();
const { setWalkMode } = useCameraModeStore();
const { setIsVersionSaved } = useSaveVersion();
const { isLogListVisible, setIsLogListVisible } = useLogger();
const { hidePlayer, setHidePlayer } = usePlayerStore();
const { setViewSceneLabels } = useViewSceneStore();
const { isRenameMode, setIsRenameMode } = useRenameModeStore();
const { selectedFloorItem } = useSelectedFloorItem();
const { selectedAssets } = useSelectedAssets();
const { setCreateNewVersion } = useVersionHistoryStore();
const { setVersionHistoryVisible } = useVersionHistoryVisibleStore();
const { setSelectedComment } = useSelectedComment();
const { setDfxUploaded } = useDfxUpload();
const isTextInput = (element: Element | null): boolean =>
element instanceof HTMLInputElement ||
element instanceof HTMLTextAreaElement ||
element?.getAttribute("contenteditable") === "true";
const { comparisonProduct, clearComparisonProduct } = useComparisonProduct();
const { activeModule, setActiveModule } = useModuleStore();
const { setSubModule } = useSubModuleStore();
const { setActiveSubTool } = useActiveSubTool();
const { toggleUILeft, toggleUIRight, setToggleUI } = useToggleStore();
const { setToggleThreeD } = useThreeDStore();
const { setToolMode } = useToolMode();
const { isPlaying, setIsPlaying } = usePlayButtonStore();
const { toggleView, setToggleView } = useToggleView();
const { setAddAction } = useAddAction();
const { setActiveTool } = useActiveTool();
const { clearSelectedZone } = useSelectedZoneStore();
const { showShortcuts, setShowShortcuts } = useShortcutStore();
const { setWalkMode } = useCameraModeStore();
const { setIsVersionSaved } = useSaveVersion();
const { isLogListVisible, setIsLogListVisible } = useLogger();
const { hidePlayer, setHidePlayer } = usePlayerStore();
const { setViewSceneLabels } = useViewSceneStore();
const { isRenameMode, setIsRenameMode } = useRenameModeStore();
const { selectedFloorAsset, setSelectedWallAsset } = useBuilderStore();
const { selectedAssets } = useSelectedAssets();
const { setCreateNewVersion } = useVersionHistoryStore();
const { setVersionHistoryVisible } = useVersionHistoryVisibleStore();
const { setSelectedComment } = useSelectedComment();
const { setDfxUploaded } = useDfxUpload();
const isTextInput = (element: Element | null): boolean => element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element?.getAttribute("contenteditable") === "true";
const handleModuleSwitch = (keyCombination: string) => {
const modules: Record<string, string> = {
"1": "builder",
"2": "simulation",
"3": "visualization",
"4": "market",
};
const module = modules[keyCombination];
if (module && !toggleView) {
setActiveTool("cursor");
setActiveSubTool("cursor");
if (module === "market") setToggleUI(false, false);
setActiveModule(module);
}
};
const handlePrimaryTools = (key: string) => {
const toolMap: Record<string, string> = {
V: "cursor",
X: "delete",
H: "free-hand",
};
const tool = toolMap[key];
if (tool && selectedAssets.length === 0) {
setActiveTool(tool);
setActiveSubTool(tool);
}
};
const handleBuilderShortcuts = (key: string) => {
if (activeModule !== "builder" || isPlaying) return;
if (key === "TAB") {
const toggleTo2D = toggleView;
setToggleView(!toggleTo2D);
setToggleThreeD(toggleTo2D);
if (toggleTo2D) {
setSelectedWallItem(null);
setAddAction(null);
setToggleUI(
localStorage.getItem("navBarUiLeft") !== "false",
localStorage.getItem("navBarUiRight") !== "false"
);
} else {
setToggleUI(false, false);
}
setActiveTool("cursor");
setActiveSubTool("cursor");
return;
}
// These should only apply in 2D view
const twoDToolConfigs: Record<string, { tool: string; mode: string }> = {
Q: { tool: "draw-wall", mode: "Wall" },
"6": { tool: "draw-wall", mode: "Wall" },
R: { tool: "draw-aisle", mode: "Aisle" },
"7": { tool: "draw-aisle", mode: "Aisle" },
E: { tool: "draw-zone", mode: "Zone" },
"8": { tool: "draw-zone", mode: "Zone" },
T: { tool: "draw-floor", mode: "Floor" },
"9": { tool: "draw-floor", mode: "Floor" },
};
const config = twoDToolConfigs[key];
if (toggleView && config) {
setActiveTool(config.tool);
setToolMode(config.mode);
}
// Measurement tool should work in both 2D and 3D
if (key === "M") {
setActiveTool("measure");
setToolMode("MeasurementScale");
}
if (key === "C") {
setActiveTool("comment");
}
};
const handleSidebarShortcuts = (key: string) => {
if (activeModule === "market") return;
const updateLocalStorage = (left: boolean, right: boolean) => {
localStorage.setItem("navBarUiLeft", JSON.stringify(left));
localStorage.setItem("navBarUiRight", JSON.stringify(right));
};
switch (key) {
case "Ctrl+\\":
if (toggleUILeft === toggleUIRight) {
const newState = !toggleUILeft;
setToggleUI(newState, newState);
updateLocalStorage(newState, newState);
} else {
setToggleUI(true, true);
updateLocalStorage(true, true);
const handleModuleSwitch = (keyCombination: string) => {
const modules: Record<string, string> = {
"1": "builder",
"2": "simulation",
"3": "visualization",
"4": "market",
};
const module = modules[keyCombination];
if (module && !toggleView) {
setActiveTool("cursor");
setActiveSubTool("cursor");
if (module === "market") setToggleUI(false, false);
setActiveModule(module);
}
break;
};
case "Ctrl+]":
setToggleUI(toggleUILeft, !toggleUIRight);
updateLocalStorage(toggleUILeft, !toggleUIRight);
break;
case "Ctrl+[":
setToggleUI(!toggleUILeft, toggleUIRight);
updateLocalStorage(!toggleUILeft, toggleUIRight);
break;
case "Ctrl+Alt+S":
setCreateNewVersion(true);
setVersionHistoryVisible(true);
setSubModule("properties");
setActiveModule("builder");
break;
case "Ctrl+H":
if (!isPlaying) {
setVersionHistoryVisible(true);
setSubModule("properties");
setActiveModule("builder");
const handlePrimaryTools = (key: string) => {
const toolMap: Record<string, string> = {
V: "cursor",
X: "delete",
H: "free-hand",
};
const tool = toolMap[key];
if (tool && selectedAssets.length === 0) {
setActiveTool(tool);
setActiveSubTool(tool);
}
break;
};
default:
break;
}
};
const handleBuilderShortcuts = (key: string) => {
if (activeModule !== "builder" || isPlaying) return;
const handleKeyPress = (event: KeyboardEvent) => {
const keyCombination = detectModifierKeys(event);
if (key === "TAB") {
const toggleTo2D = toggleView;
setToggleView(!toggleTo2D);
setToggleThreeD(toggleTo2D);
if (toggleTo2D) {
setSelectedWallAsset(null);
setAddAction(null);
setToggleUI(
localStorage.getItem("navBarUiLeft") !== "false",
localStorage.getItem("navBarUiRight") !== "false"
);
} else {
setToggleUI(false, false);
}
setActiveTool("cursor");
setActiveSubTool("cursor");
return;
}
if (isTextInput(document.activeElement) && keyCombination !== "ESCAPE") return;
// These should only apply in 2D view
const twoDToolConfigs: Record<string, { tool: string; mode: string }> = {
Q: { tool: "draw-wall", mode: "Wall" },
"6": { tool: "draw-wall", mode: "Wall" },
R: { tool: "draw-aisle", mode: "Aisle" },
"7": { tool: "draw-aisle", mode: "Aisle" },
E: { tool: "draw-zone", mode: "Zone" },
"8": { tool: "draw-zone", mode: "Zone" },
T: { tool: "draw-floor", mode: "Floor" },
"9": { tool: "draw-floor", mode: "Floor" },
};
if (keyCombination === "ESCAPE") {
setWalkMode(false);
setActiveTool("cursor");
setActiveSubTool("cursor");
setIsPlaying(false);
clearSelectedZone();
setShowShortcuts(false);
setIsVersionSaved(false);
clearComparisonProduct();
setIsLogListVisible(false);
setIsRenameMode(false);
setDfxUploaded([]);
setSelectedComment(null);
}
const config = twoDToolConfigs[key];
if (toggleView && config) {
setActiveTool(config.tool);
setToolMode(config.mode);
}
if (
!keyCombination ||
["F5", "F11", "F12"].includes(event.key) ||
keyCombination === "Ctrl+R"
)
return;
// Measurement tool should work in both 2D and 3D
if (key === "M") {
setActiveTool("measure");
setToolMode("MeasurementScale");
}
if (key === "C") {
setActiveTool("comment");
}
};
event.preventDefault();
const handleSidebarShortcuts = (key: string) => {
if (activeModule === "market") return;
// Shortcuts specific for sidebar visibility toggle and others specific to sidebar if added
handleSidebarShortcuts(keyCombination);
const updateLocalStorage = (left: boolean, right: boolean) => {
localStorage.setItem("navBarUiLeft", JSON.stringify(left));
localStorage.setItem("navBarUiRight", JSON.stringify(right));
};
switch (key) {
case "Ctrl+\\":
if (toggleUILeft === toggleUIRight) {
const newState = !toggleUILeft;
setToggleUI(newState, newState);
updateLocalStorage(newState, newState);
} else {
setToggleUI(true, true);
updateLocalStorage(true, true);
}
break;
case "Ctrl+]":
setToggleUI(toggleUILeft, !toggleUIRight);
updateLocalStorage(toggleUILeft, !toggleUIRight);
break;
case "Ctrl+[":
setToggleUI(!toggleUILeft, toggleUIRight);
updateLocalStorage(!toggleUILeft, toggleUIRight);
break;
case "Ctrl+Alt+S":
setCreateNewVersion(true);
setVersionHistoryVisible(true);
setSubModule("properties");
setActiveModule("builder");
break;
case "Ctrl+H":
if (!isPlaying) {
setVersionHistoryVisible(true);
setSubModule("properties");
setActiveModule("builder");
}
break;
default:
break;
}
};
const handleKeyPress = (event: KeyboardEvent) => {
const keyCombination = detectModifierKeys(event);
if (isTextInput(document.activeElement) && keyCombination !== "ESCAPE") return;
if (keyCombination === "ESCAPE") {
setWalkMode(false);
setActiveTool("cursor");
setActiveSubTool("cursor");
setIsPlaying(false);
clearSelectedZone();
setShowShortcuts(false);
setIsVersionSaved(false);
clearComparisonProduct();
setIsLogListVisible(false);
setIsRenameMode(false);
setDfxUploaded([]);
setSelectedComment(null);
}
if (!keyCombination || ["F5", "F11", "F12"].includes(event.key) || keyCombination === "Ctrl+R")
return;
event.preventDefault();
// Shortcuts specific for sidebar visibility toggle and others specific to sidebar if added
handleSidebarShortcuts(keyCombination);
// Active module selection (builder, simulation, etc.)
if (event.location === 0) { // Location 0 = standard keyboard (not numpad)
handleModuleSwitch(keyCombination);
}
// Common editing tools: cursor | delete | free-hand
handlePrimaryTools(keyCombination);
// Shortcuts specific to the builder module (e.g., drawing and measurement tools)
handleBuilderShortcuts(keyCombination);
// Active module selection (builder, simulation, etc.)
if (event.location === 0) { // Location 0 = standard keyboard (not numpad)
handleModuleSwitch(keyCombination);
}
// Common editing tools: cursor | delete | free-hand
handlePrimaryTools(keyCombination);
// Shortcuts specific to the builder module (e.g., drawing and measurement tools)
handleBuilderShortcuts(keyCombination);
// Shortcut to enter play mode
if (keyCombination === "Ctrl+P" && !toggleView && !comparisonProduct) {
setIsPlaying(true);
}
// Shortcut to enter play mode
if (keyCombination === "Ctrl+P" && !toggleView && !comparisonProduct) {
setIsPlaying(true);
}
if (keyCombination === "L") {
setIsLogListVisible(!isLogListVisible);
}
if (keyCombination === "L") {
setIsLogListVisible(!isLogListVisible);
}
if (keyCombination === "H") {
setHidePlayer(!hidePlayer);
}
if (keyCombination === "H") {
setHidePlayer(!hidePlayer);
}
if (keyCombination === "Ctrl+Shift+?") {
setShowShortcuts(!showShortcuts);
}
if (keyCombination === "Ctrl+Shift+?") {
setShowShortcuts(!showShortcuts);
}
// if (keyCombination === "Ctrl+Shift+P") {
// pref
// }
// if (keyCombination === "Ctrl+Shift+P") {
// pref
// }
if (keyCombination === "U") {
setViewSceneLabels((prev) => !prev);
}
if (keyCombination === "U") {
setViewSceneLabels((prev) => !prev);
}
if ((selectedFloorItem || selectedAssets.length === 1) && keyCombination === "F2") {
setIsRenameMode(true);
}
if ((selectedFloorAsset || selectedAssets.length === 1) && keyCombination === "F2") {
setIsRenameMode(true);
}
// Placeholder for future implementation
if (
["Ctrl+Z", "Ctrl+Y", "Ctrl+Shift+Z", "Ctrl+F"].includes(keyCombination)
) {
// Implement undo/redo/help/find/shortcuts
}
};
// Placeholder for future implementation
if (["Ctrl+Z", "Ctrl+Y", "Ctrl+Shift+Z", "Ctrl+F"].includes(keyCombination)) {
// Implement undo/redo/help/find/shortcuts
}
};
useEffect(() => {
window.addEventListener("keydown", handleKeyPress);
return () => window.removeEventListener("keydown", handleKeyPress);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
activeModule,
toggleUIRight,
toggleUILeft,
toggleView,
showShortcuts,
isPlaying,
isLogListVisible,
hidePlayer,
selectedFloorItem,
isRenameMode,
selectedAssets
]);
useEffect(() => {
window.addEventListener("keydown", handleKeyPress);
return () => window.removeEventListener("keydown", handleKeyPress);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeModule, toggleUIRight, toggleUILeft, toggleView, showShortcuts, isPlaying, isLogListVisible, hidePlayer, selectedFloorAsset, isRenameMode, selectedAssets]);
return null;
return null;
};
export default KeyPressListener;