Refactor version management: Replace useSaveVersion with useIsComparing across components

- Updated MainScene, SideBarLeft, SideBarRight, Simulations, and CompareLayOut to utilize isComparing state instead of isVersionSaved.
- Adjusted conditional rendering and logic to reflect the new comparison state.
- Introduced SyncCam component to synchronize camera state during comparisons.
- Created useSceneStore to manage camera state with position and target vectors.
- Cleaned up imports and ensured consistent formatting across affected files.
This commit is contained in:
2025-09-05 18:09:43 +05:30
parent 8afa92cbeb
commit 0f10a84215
15 changed files with 288 additions and 291 deletions

View File

@@ -10,7 +10,6 @@ import ForgotPassword from "./pages/ForgotPassword";
import PageNotFound from "./pages/PageNotFound";
const App: React.FC = () => {
useEffect(() => {
Cache.clear();
Cache.enabled = true;

View File

@@ -1,5 +1,5 @@
import { useParams } from "react-router-dom";
import { useCompareProductDataStore, useLoadingProgress, useSaveVersion } from "../../../store/builder/store";
import { useCompareProductDataStore, useLoadingProgress, useIsComparing } from "../../../store/builder/store";
import { useComparisonProduct, useMainProduct } from "../../../store/simulation/useSimulationStore";
import { usePlayButtonStore } from "../../../store/ui/usePlayButtonStore";
import { useEffect, useState } from "react";
@@ -9,21 +9,18 @@ import CompareLayOut from "../../ui/compareVersion/CompareLayOut";
import ComparisonResult from "../../ui/compareVersion/ComparisonResult";
import RegularDropDown from "../../ui/inputs/RegularDropDown";
import { getVersionHistoryApi } from "../../../services/factoryBuilder/versionControl/getVersionHistoryApi";
function ComparisonScene() {
const { isPlaying } = usePlayButtonStore();
const { productStore, versionStore } = useSceneContext();
const { versionHistory, selectedVersion, setSelectedVersion, setVersions } = versionStore();
const { versionHistory, selectedVersion, setSelectedVersion } = versionStore();
const { products, selectedProduct } = productStore();
const { isVersionSaved } = useSaveVersion();
const { isComparing } = useIsComparing();
const { activeModule } = useModuleStore();
const { comparisonProduct, setComparisonProduct } = useComparisonProduct();
const { mainProduct } = useMainProduct();
const { loadingProgress } = useLoadingProgress();
const { compareProductsData } = useCompareProductDataStore();
const [shouldShowComparisonResult, setShouldShowComparisonResult] = useState(false);
const { projectId } = useParams();
const handleSelectVersion = (option: string) => {
const version = versionHistory.find((version) => version.versionName === option);
@@ -39,30 +36,6 @@ function ComparisonScene() {
}
};
useEffect(() => {
if (!projectId) return;
getVersionHistoryApi(projectId)
.then((data) => {
const versions: VersionHistory = [];
data.versions.forEach((version: any) => {
versions.push({
version: version.version,
versionId: version.versionId,
versionName: version.versionName,
versionDescription: version.description,
timeStamp: version.createdAt,
createdBy: version.createdBy.userName,
});
});
setVersions(versions);
})
.catch(() => {
console.error("Error fetching version history");
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [projectId]);
useEffect(() => {
if (mainProduct && comparisonProduct && compareProductsData.length > 1) {
const hasMain = compareProductsData.some((val) => val.productUuid === mainProduct.productUuid);
@@ -79,7 +52,7 @@ function ComparisonScene() {
return (
<>
{isVersionSaved && activeModule === "simulation" && selectedProduct && (
{isComparing && activeModule === "simulation" && selectedProduct && (
<>
{selectedVersion && !isPlaying && (
<div className="initial-selectLayout-wrapper">

View File

@@ -1,6 +1,6 @@
import { useEffect } from "react";
import { useParams } from "react-router-dom";
import { useLoadingProgress, useRenameModeStore, useSaveVersion, useSelectedComment, useSocketStore, useWidgetSubOption } from "../../../store/builder/store";
import { useLoadingProgress, useRenameModeStore, useIsComparing, useSelectedComment, useSocketStore, useWidgetSubOption } from "../../../store/builder/store";
import useModuleStore, { useThreeDStore } from "../../../store/ui/useModuleStore";
import { usePlayButtonStore } from "../../../store/ui/usePlayButtonStore";
import { useSelectedZoneStore } from "../../../store/visualization/useZoneStore";
@@ -36,7 +36,7 @@ import { getVersionHistoryApi } from "../../../services/factoryBuilder/versionCo
function MainScene() {
const { setMainProduct } = useMainProduct();
const { isVersionSaved, setIsVersionSaved } = useSaveVersion();
const { isComparing, setIsComparing } = useIsComparing();
const { activeModule } = useModuleStore();
const { selectedUser } = useSelectedUserStore();
const { loadingProgress } = useLoadingProgress();
@@ -67,9 +67,9 @@ function MainScene() {
useEffect(() => {
if (activeModule !== "simulation") {
clearComparisonProduct();
setIsVersionSaved(false);
setIsComparing(false);
}
}, [activeModule, clearComparisonProduct, setIsVersionSaved]);
}, [activeModule, clearComparisonProduct, setIsComparing]);
useEffect(() => {
if (!projectId) return;
@@ -162,14 +162,14 @@ function MainScene() {
{loadingProgress > 0 && <LoadingPage progress={loadingProgress} />}
{!isPlaying && (
<>
{toggleThreeD && !isVersionSaved && <ModuleToggle />}
{toggleThreeD && !isComparing && <ModuleToggle />}
<SideBarLeft />
<SideBarRight />
</>
)}
<RealTimeVisulization />
{activeModule === "market" && <MarketPlace />}
{activeModule !== "market" && !isPlaying && !isVersionSaved && <Tools />}
{activeModule !== "market" && !isPlaying && !isComparing && <Tools />}
{isPlaying && activeModule === "simulation" && loadingProgress === 0 && <SimulationPlayer />}
{isPlaying && activeModule !== "simulation" && <ControlsPlayer />}
@@ -204,7 +204,7 @@ function MainScene() {
<Scene layout="Main Layout" />
</div>
{selectedProduct && selectedVersion && isVersionSaved && !isPlaying && activeModule === "simulation" && (
{selectedProduct && selectedVersion && isComparing && !isPlaying && activeModule === "simulation" && (
<div className="selectLayout-wrapper">
<RegularDropDown
header={selectedVersion.versionName}

View File

@@ -8,7 +8,7 @@ import useModuleStore from "../../../store/ui/useModuleStore";
import Widgets from "./visualization/widgets/Widgets";
import Templates from "../../../modules/visualization/template/Templates";
import Search from "../../ui/inputs/Search";
import { useSaveVersion } from "../../../store/builder/store";
import { useIsComparing } from "../../../store/builder/store";
const SideBarLeft: React.FC = () => {
const [activeOption, setActiveOption] = useState("Widgets");
@@ -16,7 +16,7 @@ const SideBarLeft: React.FC = () => {
const { toggleUILeft } = useToggleStore();
const { activeModule } = useModuleStore();
const { isVersionSaved } = useSaveVersion();
const { isComparing } = useIsComparing();
// Reset activeOption whenever activeModule changes
useEffect(() => {
@@ -35,7 +35,7 @@ const SideBarLeft: React.FC = () => {
return (
<div
className={`sidebar-left-wrapper ${toggleUILeft && (!isVersionSaved || activeModule !== "simulation") ? "open" : "closed"
className={`sidebar-left-wrapper ${toggleUILeft && (!isComparing || activeModule !== "simulation") ? "open" : "closed"
}`}
>
<Header />
@@ -74,7 +74,7 @@ const SideBarLeft: React.FC = () => {
} else {
return (
<>
{!isVersionSaved && (
{!isComparing && (
<>
<ToggleHeader
options={["Outline"]}

View File

@@ -6,7 +6,7 @@ import { useToggleStore } from "../../../store/ui/useUIToggleStore";
import Visualization from "./visualization/Visualization";
import Analysis from "./analysis/Analysis";
import Simulations from "./simulation/Simulations";
import useVersionHistoryVisibleStore, { useSaveVersion, useToolMode } from "../../../store/builder/store";
import useVersionHistoryVisibleStore, { useIsComparing, useToolMode } from "../../../store/builder/store";
import { useSelectedEventData, useSelectedEventSphere, } from "../../../store/simulation/useSimulationStore";
import { useBuilderStore } from "../../../store/builder/useBuilderStore";
import GlobalProperties from "./properties/GlobalProperties";
@@ -52,7 +52,7 @@ const SideBarRight: React.FC = () => {
const { selectedEventData } = useSelectedEventData();
const { selectedEventSphere } = useSelectedEventSphere();
const { viewVersionHistory, setVersionHistoryVisible } = useVersionHistoryVisibleStore();
const { isVersionSaved } = useSaveVersion();
const { isComparing } = useIsComparing();
const [displayComponent, setDisplayComponent] = useState<DisplayComponent>("none");
@@ -80,7 +80,7 @@ const SideBarRight: React.FC = () => {
return;
}
if (!isVersionSaved && activeModule === "simulation") {
if (!isComparing && activeModule === "simulation") {
if (subModule === "simulations") {
setDisplayComponent("simulations");
return;
@@ -155,7 +155,7 @@ const SideBarRight: React.FC = () => {
}
setDisplayComponent("none");
}, [viewVersionHistory, activeModule, subModule, isVersionSaved, selectedFloorAsset, selectedWall, selectedFloor, selectedAisle, toolMode, selectedDecal]);
}, [viewVersionHistory, activeModule, subModule, isComparing, selectedFloorAsset, selectedWall, selectedFloor, selectedAisle, toolMode, selectedDecal]);
const renderComponent = () => {
switch (displayComponent) {
@@ -197,11 +197,11 @@ const SideBarRight: React.FC = () => {
};
return (
<div className={`sidebar-right-wrapper ${toggleUIRight && (!isVersionSaved || activeModule !== "simulation") ? "open" : "closed"}`} onPointerDown={(e) => e.stopPropagation()}>
<div className={`sidebar-right-wrapper ${toggleUIRight && (!isComparing || activeModule !== "simulation") ? "open" : "closed"}`} onPointerDown={(e) => e.stopPropagation()}>
<Header />
{toggleUIRight && (
<>
{(!isVersionSaved || activeModule !== "simulation") && (
{(!isComparing || activeModule !== "simulation") && (
<div className="sidebar-actions-container">
{activeModule !== "simulation" && (
<>

View File

@@ -13,7 +13,7 @@ import { deleteProductApi } from "../../../../services/simulation/products/delet
import { renameProductApi } from "../../../../services/simulation/products/renameProductApi";
import { determineExecutionMachineSequences } from "../../../../modules/simulation/simulator/functions/determineExecutionMachineSequences";
import ComparePopUp from "../../../ui/compareVersion/Compare";
import { useCompareStore, useSaveVersion } from "../../../../store/builder/store";
import { useCompareStore, useIsComparing } from "../../../../store/builder/store";
import { useToggleStore } from "../../../../store/ui/useUIToggleStore";
import { useParams } from "react-router-dom";
import { useSceneContext } from "../../../../modules/scene/sceneContext";
@@ -36,10 +36,10 @@ const Simulations: React.FC = () => {
const { setMainProduct } = useMainProduct();
const { selectedVersion } = versionStore();
const { comparePopUp, setComparePopUp } = useCompareStore();
const { setIsVersionSaved } = useSaveVersion();
const { setIsComparing } = useIsComparing();
const handleSaveVersion = () => {
setIsVersionSaved(true);
setIsComparing(true);
setComparePopUp(false);
setToggleUI(false, false);
};

View File

@@ -1,19 +1,22 @@
import { useParams } from "react-router-dom";
import React, { useState, useRef, useEffect, Suspense } from "react";
import { CompareLayoutIcon, LayoutIcon, ResizerIcon } from "../../icons/SimulationIcons";
import { useLoadingProgress, useSaveVersion } from "../../../store/builder/store";
import Search from "../inputs/Search";
import OuterClick from "../../../utils/outerClick";
import Scene from "../../../modules/scene/scene";
import { useLoadingProgress, useIsComparing } from "../../../store/builder/store";
import { useComparisonProduct } from "../../../store/simulation/useSimulationStore";
import { usePlayButtonStore } from "../../../store/ui/usePlayButtonStore";
import { useSceneContext } from "../../../modules/scene/sceneContext";
import { getAllProductsApi } from "../../../services/simulation/products/getallProductsApi";
import { useParams } from "react-router-dom";
import Search from "../inputs/Search";
import OuterClick from "../../../utils/outerClick";
import Scene from "../../../modules/scene/scene";
import useRestStates from "../../../hooks/useResetStates";
import { getVersionHistoryApi } from "../../../services/factoryBuilder/versionControl/getVersionHistoryApi";
const CompareLayOut = () => {
const { clearComparisonProduct, comparisonProduct, setComparisonProduct } = useComparisonProduct();
const { versionStore } = useSceneContext();
const { versionHistory, selectedVersion, setSelectedVersion, clearSelectedVersion } = versionStore();
const { versionHistory, selectedVersion, setSelectedVersion, clearSelectedVersion, setVersions } = versionStore();
const { setLoadingProgress } = useLoadingProgress();
const [width, setWidth] = useState("50vw");
const [isResizing, setIsResizing] = useState(false);
@@ -21,10 +24,43 @@ const CompareLayOut = () => {
const wrapperRef = useRef<HTMLDivElement>(null);
const startWidthRef = useRef<number>(0);
const startXRef = useRef<number>(0);
const { setIsVersionSaved } = useSaveVersion();
const { setIsComparing } = useIsComparing();
const { loadingProgress } = useLoadingProgress();
const { setIsPlaying } = usePlayButtonStore();
const { projectId } = useParams();
const { resetStates } = useRestStates();
useEffect(() => {
return () => {
if (selectedVersion?.versionId) {
resetStates();
}
};
}, [selectedVersion?.versionId]);
useEffect(() => {
if (!projectId) return;
getVersionHistoryApi(projectId)
.then((data) => {
const versions: VersionHistory = [];
data.versions.forEach((version: any) => {
versions.push({
version: version.version,
versionId: version.versionId,
versionName: version.versionName,
versionDescription: version.description,
timeStamp: version.createdAt,
createdBy: version.createdBy.userName,
});
});
setVersions(versions);
})
.catch(() => {
console.error("Error fetching version history");
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [projectId]);
useEffect(() => {
if (!comparisonProduct) {
@@ -71,7 +107,7 @@ const CompareLayOut = () => {
if (finalWidthVw <= 10) {
setWidth("0px");
setIsVersionSaved(false);
setIsComparing(false);
clearComparisonProduct();
setIsPlaying(false);
} else {

View File

@@ -1,17 +1,17 @@
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 { useToggleView, useToolMode } from '../../../../../store/builder/store';
import { AssetBoundingBox } from '../../functions/assetBoundingBox';
import { useBuilderStore } from '../../../../../store/builder/useBuilderStore';
import useModuleStore from '../../../../../store/ui/useModuleStore';
import { useSceneContext } from '../../../../scene/sceneContext';
import { SkeletonUtils } from 'three-stdlib';
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 { useToggleView, useToolMode } from "../../../../../store/builder/store";
import { AssetBoundingBox } from "../../functions/assetBoundingBox";
import { useBuilderStore } from "../../../../../store/builder/useBuilderStore";
import useModuleStore from "../../../../../store/ui/useModuleStore";
import { useSceneContext } from "../../../../scene/sceneContext";
import { SkeletonUtils } from "three-stdlib";
import { getAssetFieldApi } from '../../../../../services/factoryBuilder/asset/floorAsset/getAssetField';
import { ModelAnimator } from './animator/modelAnimator';
import { useModelEventHandlers } from './eventHandlers/useModelEventHandlers';
import { getAssetFieldApi } from "../../../../../services/factoryBuilder/asset/floorAsset/getAssetField";
import { ModelAnimator } from "./animator/modelAnimator";
import { useModelEventHandlers } from "./eventHandlers/useModelEventHandlers";
function Model({ asset, isRendered, loader }: Readonly<{ asset: Asset; isRendered: boolean; loader: GLTFLoader }>) {
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
@@ -31,33 +31,33 @@ function Model({ asset, isRendered, loader }: Readonly<{ asset: Asset; isRendere
useEffect(() => {
if (!fieldData && asset.eventData) {
getAssetFieldApi(asset.assetId).then((data) => {
if (data.type === 'ArmBot') {
if (data.type === "ArmBot") {
if (data.data) {
const fieldData: IK[] = data.data;
setFieldData(fieldData);
}
} else if (data.type === 'Conveyor' || data.type === 'Crane') {
} else if (data.type === "Conveyor" || data.type === "Crane") {
if (data.data) {
const fieldData = data.data;
setFieldData(fieldData);
}
}
})
});
}
}, [asset.modelUuid, fieldData])
}, [asset.modelUuid, fieldData]);
useEffect(() => {
setDeletableFloorAsset(null);
if (selectedFloorAsset === null || selectedFloorAsset.userData.modelUuid !== asset.modelUuid) {
resetAnimation(asset.modelUuid);
}
}, [activeModule, toolMode, selectedFloorAsset])
}, [activeModule, toolMode, selectedFloorAsset]);
useEffect(() => {
if (selectedFloorAsset && selectedFloorAsset.userData.modelUuid === asset.modelUuid) {
setSelectedFloorAsset(groupRef.current);
}
}, [isRendered, selectedFloorAsset])
}, [isRendered, selectedFloorAsset]);
useEffect(() => {
if (selectedAssets.length > 0) {
@@ -69,7 +69,7 @@ function Model({ asset, isRendered, loader }: Readonly<{ asset: Asset; isRendere
} else {
setIsSelected(false);
}
}, [selectedAssets])
}, [selectedAssets]);
useEffect(() => {
if (gltfScene) {
@@ -78,13 +78,13 @@ function Model({ asset, isRendered, loader }: Readonly<{ asset: Asset; isRendere
child.castShadow = true;
child.receiveShadow = true;
}
})
});
}
}, [gltfScene]);
const logModelStatus = (modelId: string, status: string) => {
// console.log(modelId, status);
}
};
useEffect(() => {
// Calculate Bounding Box
@@ -101,12 +101,13 @@ function Model({ asset, isRendered, loader }: Readonly<{ asset: Asset; isRendere
clone.animations = cachedModel.animations || [];
setGltfScene(clone);
calculateBoundingBox(clone);
logModelStatus(assetId, 'cache-loaded');
logModelStatus(assetId, "cache-loaded");
return;
}
// Check IndexedDB
retrieveGLTF(assetId).then((indexedDBModel) => {
retrieveGLTF(assetId)
.then((indexedDBModel) => {
if (indexedDBModel) {
const blobUrl = URL.createObjectURL(indexedDBModel);
loader.load(
@@ -117,10 +118,10 @@ function Model({ asset, isRendered, loader }: Readonly<{ asset: Asset; isRendere
THREE.Cache.add(assetId, gltf);
setGltfScene(gltf.scene.clone());
calculateBoundingBox(gltf.scene);
logModelStatus(assetId, 'indexedDB-loaded');
logModelStatus(assetId, "indexedDB-loaded");
},
undefined,
(error) => {
() => {
echo.error(`[IndexedDB] Error loading ${asset.modelName}:`);
URL.revokeObjectURL(blobUrl);
}
@@ -140,21 +141,19 @@ function Model({ asset, isRendered, loader }: Readonly<{ asset: Asset; isRendere
THREE.Cache.add(assetId, gltf);
setGltfScene(gltf.scene.clone());
calculateBoundingBox(gltf.scene);
logModelStatus(assetId, 'backend-loaded');
logModelStatus(assetId, "backend-loaded");
})
.catch((error) => {
console.error(
`[Backend] Error storing/loading ${asset.modelName}:`,
error
);
console.error(`[Backend] Error storing/loading ${asset.modelName}:`, error);
});
},
undefined,
(error) => {
() => {
echo.error(`[Backend] Error loading ${asset.modelName}:`);
}
);
}).catch((err) => {
})
.catch((err) => {
console.error("Failed to load model:", asset.assetId, err);
});
}, []);
@@ -164,7 +163,7 @@ function Model({ asset, isRendered, loader }: Readonly<{ asset: Asset; isRendere
return (
<group
key={asset.modelUuid}
name='Asset Model'
name="Asset Model"
ref={groupRef}
uuid={asset.modelUuid}
position={asset.position}
@@ -206,22 +205,14 @@ function Model({ asset, isRendered, loader }: Readonly<{ asset: Asset; isRendere
<>
{isRendered ? (
<>
<primitive object={gltfScene} />
<ModelAnimator asset={asset} gltfScene={gltfScene} />
</>
) : (
<>
{!isSelected &&
<AssetBoundingBox name='Asset Fallback' boundingBox={boundingBox} color='gray' lineWidth={2.5} />
}
</>
<>{!isSelected && <AssetBoundingBox name="Asset Fallback" boundingBox={boundingBox} color="gray" lineWidth={2.5} />}</>
)}
{isSelected &&
<AssetBoundingBox name='Asset BBox' boundingBox={boundingBox} color={savedTheme === "dark" ? "#c4abf1" : "#6f42c1"} lineWidth={2.7} />
}
{isSelected && <AssetBoundingBox name="Asset BBox" boundingBox={boundingBox} color={savedTheme === "dark" ? "#c4abf1" : "#6f42c1"} lineWidth={2.7} />}
</>
)}
</group>

View File

@@ -1,13 +1,13 @@
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 } from '../../../../store/builder/store';
import { useSelectedAsset } from '../../../../store/simulation/useSimulationStore';
import { useSceneContext } from '../../../scene/sceneContext';
import { CameraControls } from "@react-three/drei";
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 Model from "./model/model";
import { GLTFLoader } from "three/examples/jsm/Addons";
const distanceWorker = new Worker(new URL("../../../../services/factoryBuilder/webWorkers/distanceWorker.js", import.meta.url));
@@ -15,7 +15,7 @@ const distanceWorker = new Worker(new URL("../../../../services/factoryBuilder/w
function Models({ loader }: { readonly loader: GLTFLoader }) {
const { controls, camera } = useThree();
const assetGroupRef = useRef<Group>(null);
const { assetStore } = useSceneContext();
const { assetStore, layout } = useSceneContext();
const { assets } = assetStore();
const { selectedFloorAsset, setSelectedFloorAsset } = useBuilderStore();
const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
@@ -40,13 +40,13 @@ function Models({ loader }: { readonly loader: GLTFLoader }) {
camera.getWorldPosition(cameraPos.current);
for (const asset of assets) {
const isRendered = renderMap[asset.modelUuid] ?? false;
distanceWorker.postMessage({ modelUuid: asset.modelUuid, assetPosition: { x: asset.position[0], y: asset.position[1], z: asset.position[2], }, cameraPosition: cameraPos.current, limitDistance, renderDistance, isRendered, });
distanceWorker.postMessage({ modelUuid: asset.modelUuid, assetPosition: { x: asset.position[0], y: asset.position[1], z: asset.position[2] }, cameraPosition: cameraPos.current, limitDistance, renderDistance, isRendered });
}
});
return (
<group
name='Asset Group'
name="Asset Group"
ref={assetGroupRef}
onPointerMissed={(e) => {
e.stopPropagation();

View File

@@ -0,0 +1,32 @@
import { useFrame, useThree } from "@react-three/fiber";
import { useSceneContext } from "../sceneContext";
import { CameraControls } from "@react-three/drei";
import { useIsComparing } from "../../../store/builder/store";
import useModuleStore from "../../../store/ui/useModuleStore";
import { useComparisonProduct } from "../../../store/simulation/useSimulationStore";
import { useSceneStore } from "../../../store/scene/useSceneStore";
import { Vector3 } from "three";
function SyncCam() {
const { layout } = useSceneContext();
const { controls } = useThree();
const { isComparing } = useIsComparing();
const { activeModule } = useModuleStore();
const { comparisonProduct } = useComparisonProduct();
const { setCamera, camState } = useSceneStore();
useFrame(() => {
if (layout === "Comparison Layout" && controls && camState) {
(controls as CameraControls).setLookAt(camState.position.x, camState.position.y, camState.position.z, camState.target.x, camState.target.y, camState.target.z, true);
}
if (layout === "Main Layout" && controls && isComparing && activeModule === "simulation" && comparisonProduct) {
const position = (controls as CameraControls).getPosition(new Vector3());
const target = (controls as CameraControls).getTarget(new Vector3());
setCamera(position, target);
}
});
return <></>;
}
export default SyncCam;

View File

@@ -2,7 +2,7 @@ import { CameraControls } from "@react-three/drei";
import { useRef, useEffect } from "react";
import { useThree } from "@react-three/fiber";
import * as THREE from "three";
import * as CONSTANTS from '../../../types/world/worldConstants';
import * as CONSTANTS from "../../../types/world/worldConstants";
import { useSocketStore, useToggleView, useResetCamera } from "../../../store/builder/store";
import CamMode from "../camera/camMode";
@@ -20,6 +20,7 @@ import { getUserData } from "../../../functions/getUserData";
import { getCameraApi } from "../../../services/factoryBuilder/camera/getCameraApi";
import { setCameraApi } from "../../../services/factoryBuilder/camera/setCameraApi";
import updateCamPosition from "../camera/functions/updateCameraPosition";
import SyncCam from "../camera/syncCam";
export default function Controls() {
const controlsRef = useRef<CameraControls>(null);
@@ -38,7 +39,8 @@ export default function Controls() {
}
if (!projectId) return;
getCameraApi(projectId).then((data) => {
getCameraApi(projectId)
.then((data) => {
if (data?.position && data?.target) {
controlsRef.current?.setPosition(data.position.x, data.position.y, data.position.z);
controlsRef.current?.setTarget(data.target.x, data.target.y, data.target.z);
@@ -46,7 +48,8 @@ export default function Controls() {
controlsRef.current?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
controlsRef.current?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
}
}).catch((error) => console.error("Failed to fetch camera data:", error));
})
.catch((error) => console.error("Failed to fetch camera data:", error));
}, [projectId]);
useEffect(() => {
@@ -56,12 +59,7 @@ export default function Controls() {
controlsRef.current?.rotateAzimuthTo(CONSTANTS.threeDimension.defaultAzimuth);
if (!socket?.connected) {
setCameraApi(
projectId,
new THREE.Vector3(...CONSTANTS.threeDimension.defaultPosition),
new THREE.Vector3(...CONSTANTS.threeDimension.defaultTarget),
new THREE.Vector3(...CONSTANTS.threeDimension.defaultRotation),
)
setCameraApi(projectId, new THREE.Vector3(...CONSTANTS.threeDimension.defaultPosition), new THREE.Vector3(...CONSTANTS.threeDimension.defaultTarget), new THREE.Vector3(...CONSTANTS.threeDimension.defaultRotation));
} else {
const camData = {
organization,
@@ -70,9 +68,9 @@ export default function Controls() {
target: new THREE.Vector3(...CONSTANTS.threeDimension.defaultTarget),
rotation: new THREE.Vector3(...CONSTANTS.threeDimension.defaultRotation),
socketId: socket.id,
projectId
projectId,
};
socket.emit('v1:Camera:set', camData)
socket.emit("v1:Camera:set", camData);
}
setResetCamera(false);
@@ -152,6 +150,7 @@ export default function Controls() {
<CameraShortcutsControls />
<SyncCam />
</CameraControls>
<SelectionControls3D />
@@ -165,7 +164,6 @@ export default function Controls() {
<TransformControl />
<ContextControls />
</>
);
}

View File

@@ -1,13 +1,7 @@
import { useRef, useEffect } from "react";
import { useThree } from "@react-three/fiber";
import * as THREE from "three";
import {
useAzimuth,
useElevation,
useShadows,
useSunPosition,
useTileDistance,
} from "../../../store/builder/store";
import { useAzimuth, useElevation, useShadows, useSunPosition, useTileDistance } from "../../../store/builder/store";
import * as CONSTANTS from "../../../types/world/worldConstants";
const shadowWorker = new Worker(new URL("../../../services/factoryBuilder/webWorkers/shadowWorker", import.meta.url));
@@ -67,31 +61,11 @@ export default function Shadows() {
{/* {(lightRef.current?.shadow) &&
<cameraHelper visible={shadows} args={[lightRef.current.shadow.camera]} />
} */}
<directionalLight
ref={lightRef}
castShadow={shadows}
shadow-mapSize-width={CONSTANTS.shadowConfig.shadowmapSizewidth}
shadow-mapSize-height={CONSTANTS.shadowConfig.shadowmapSizeheight}
shadow-camera-far={CONSTANTS.shadowConfig.shadowcamerafar}
shadow-camera-near={CONSTANTS.shadowConfig.shadowcameranear}
shadow-camera-top={CONSTANTS.shadowConfig.shadowcameratop}
shadow-camera-bottom={CONSTANTS.shadowConfig.shadowcamerabottom}
shadow-camera-left={CONSTANTS.shadowConfig.shadowcameraleft}
shadow-camera-right={CONSTANTS.shadowConfig.shadowcameraright}
shadow-bias={CONSTANTS.shadowConfig.shadowbias}
shadow-normalBias={CONSTANTS.shadowConfig.shadownormalBias}
/>
<directionalLight ref={lightRef} castShadow={shadows} shadow-mapSize-width={CONSTANTS.shadowConfig.shadowmapSizewidth} shadow-mapSize-height={CONSTANTS.shadowConfig.shadowmapSizeheight} shadow-camera-far={CONSTANTS.shadowConfig.shadowcamerafar} shadow-camera-near={CONSTANTS.shadowConfig.shadowcameranear} shadow-camera-top={CONSTANTS.shadowConfig.shadowcameratop} shadow-camera-bottom={CONSTANTS.shadowConfig.shadowcamerabottom} shadow-camera-left={CONSTANTS.shadowConfig.shadowcameraleft} shadow-camera-right={CONSTANTS.shadowConfig.shadowcameraright} shadow-bias={CONSTANTS.shadowConfig.shadowbias} shadow-normalBias={CONSTANTS.shadowConfig.shadownormalBias} />
<object3D ref={targetRef} />
<mesh
position={CONSTANTS.shadowConfig.shadowMaterialPosition}
rotation={CONSTANTS.shadowConfig.shadowMaterialRotation}
receiveShadow
>
<mesh position={CONSTANTS.shadowConfig.shadowMaterialPosition} rotation={CONSTANTS.shadowConfig.shadowMaterialRotation} receiveShadow>
<planeGeometry args={[planeValue.width, planeValue.height]} />
<shadowMaterial
opacity={CONSTANTS.shadowConfig.shadowMaterialOpacity}
transparent
/>
<shadowMaterial opacity={CONSTANTS.shadowConfig.shadowMaterialOpacity} transparent />
</mesh>
</>
);

View File

@@ -8,12 +8,7 @@ type SocketStore = {
dashBoardSocket?: ReturnType<typeof io> | null;
projectSocket?: ReturnType<typeof io> | null;
threadSocket?: ReturnType<typeof io> | null;
initializeSocket: (
email?: string,
organization?: string,
token?: string,
refreshToken?: string
) => void;
initializeSocket: (email?: string, organization?: string, token?: string, refreshToken?: string) => void;
disconnectSocket: () => void;
};
@@ -23,54 +18,34 @@ export const useSocketStore = create<SocketStore>((set, get) => ({
dashBoardSocket: null,
projectSocket: null,
threadSocket: null,
initializeSocket: (
email?: string,
organization?: string,
token?: string,
refreshToken?: string
) => {
initializeSocket: (email?: string, organization?: string, token?: string, refreshToken?: string) => {
const existingSocket = get().socket;
if (existingSocket) {
return;
}
const socket = io(
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder_v1`,
{
const socket = io(`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder_v1`, {
reconnection: true,
auth: { token, refreshToken },
}
);
});
const visualizationSocket = io(
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Visualization_v1`,
{
const visualizationSocket = io(`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Visualization_v1`, {
reconnection: true,
auth: { token, refreshToken },
}
);
});
const dashBoardSocket = io(
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/dashboard`,
{
const dashBoardSocket = io(`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/dashboard`, {
reconnection: true,
auth: { token, refreshToken },
}
);
const projectSocket = io(
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/project`,
{
});
const projectSocket = io(`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/project`, {
reconnection: true,
auth: { token, refreshToken },
}
);
const threadSocket = io(
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/thread`,
{
});
const threadSocket = io(`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/thread`, {
reconnection: true,
auth: { token, refreshToken },
}
);
});
set({
socket,
@@ -154,8 +129,7 @@ export const useShadows = create<any>((set: any) => ({
export const useSunPosition = create<any>((set: any) => ({
sunPosition: { x: undefined, y: undefined, z: undefined },
setSunPosition: (newSuntPosition: any) =>
set({ sunPosition: newSuntPosition }),
setSunPosition: (newSuntPosition: any) => set({ sunPosition: newSuntPosition }),
}));
export const useProjectName = create<any>((set: any) => ({
@@ -215,14 +189,12 @@ export const useRenameModeStore = create<any>((set: any) => ({
export const useObjectPosition = create<any>((set: any) => ({
objectPosition: { x: undefined, y: undefined, z: undefined },
setObjectPosition: (newObjectPosition: any) =>
set({ objectPosition: newObjectPosition }),
setObjectPosition: (newObjectPosition: any) => set({ objectPosition: newObjectPosition }),
}));
export const useObjectRotation = create<any>((set: any) => ({
objectRotation: { x: undefined, y: undefined, z: undefined },
setObjectRotation: (newObjectRotation: any) =>
set({ objectRotation: newObjectRotation }),
setObjectRotation: (newObjectRotation: any) => set({ objectRotation: newObjectRotation }),
}));
export const useDrieTemp = create<any>((set: any) => ({
@@ -234,16 +206,14 @@ export const useActiveUsers = create<any>((set: any) => ({
activeUsers: [],
setActiveUsers: (callback: (prev: any[]) => any[] | any[]) =>
set((state: { activeUsers: any[] }) => ({
activeUsers:
typeof callback === "function" ? callback(state.activeUsers) : callback,
activeUsers: typeof callback === "function" ? callback(state.activeUsers) : callback,
})),
}));
export const useDrieUIValue = create<any>((set: any) => ({
drieUIValue: { touch: null, temperature: null, humidity: null },
setDrieUIValue: (x: any) =>
set((state: any) => ({ drieUIValue: { ...state.drieUIValue, ...x } })),
setDrieUIValue: (x: any) => set((state: any) => ({ drieUIValue: { ...state.drieUIValue, ...x } })),
setTouch: (value: any) =>
set((state: any) => ({
@@ -367,8 +337,7 @@ interface ShortcutStore {
export const useShortcutStore = create<ShortcutStore>((set) => ({
showShortcuts: false,
setShowShortcuts: (value) => set({ showShortcuts: value }),
toggleShortcuts: () =>
set((state) => ({ showShortcuts: !state.showShortcuts })),
toggleShortcuts: () => set((state) => ({ showShortcuts: !state.showShortcuts })),
}));
export const useMachineCount = create<any>((set: any) => ({
@@ -470,19 +439,18 @@ interface CompareStore {
export const useCompareStore = create<CompareStore>((set) => ({
comparePopUp: false,
setComparePopUp: (value) => set({ comparePopUp: value }),
toggleComparePopUp: () =>
set((state) => ({ comparePopUp: !state.comparePopUp })),
toggleComparePopUp: () => set((state) => ({ comparePopUp: !state.comparePopUp })),
}));
// Save state store
interface SaveVersionStore {
isVersionSaved: boolean;
setIsVersionSaved: (value: boolean) => void;
interface IsComparingStore {
isComparing: boolean;
setIsComparing: (value: boolean) => void;
}
export const useSaveVersion = create<SaveVersionStore>((set) => ({
isVersionSaved: false,
setIsVersionSaved: (value: boolean) => set({ isVersionSaved: value }),
export const useIsComparing = create<IsComparingStore>((set) => ({
isComparing: false,
setIsComparing: (value: boolean) => set({ isComparing: value }),
}));
interface ViewSceneState {
@@ -494,8 +462,7 @@ export const useViewSceneStore = create<ViewSceneState>((set) => ({
viewSceneLabels: getInitialViewSceneLabels(),
setViewSceneLabels: (value) => {
set((state) => {
const newValue =
typeof value === "function" ? value(state.viewSceneLabels) : value;
const newValue = typeof value === "function" ? value(state.viewSceneLabels) : value;
// Store in localStorage manually
localStorage.setItem("viewSceneLabels", JSON.stringify(newValue));

View File

@@ -0,0 +1,27 @@
import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
import * as THREE from "three";
type SceneStore = {
camState: {
position: THREE.Vector3;
target: THREE.Vector3;
};
setCamera: (pos: THREE.Vector3, target: THREE.Vector3) => void;
};
export const useSceneStore = create<SceneStore>()(
immer((set) => ({
camState: {
position: new THREE.Vector3(0, 5, 10),
target: new THREE.Vector3(0, 0, 0),
},
setCamera: (pos, target) =>
set((state) => {
state.camState.position.copy(pos);
state.camState.target.copy(target);
}),
}))
);

View File

@@ -1,7 +1,7 @@
import React, { useEffect } from "react";
import useModuleStore, { useSubModuleStore, useThreeDStore } from "../../store/ui/useModuleStore";
import { usePlayerStore, useToggleStore } from "../../store/ui/useUIToggleStore";
import useVersionHistoryVisibleStore, { useActiveSubTool, useActiveTool, useAddAction, useDfxUpload, useRenameModeStore, useSaveVersion, useSelectedComment, useShortcutStore, useToggleView, useToolMode, useViewSceneStore } from "../../store/builder/store";
import useVersionHistoryVisibleStore, { useActiveSubTool, useActiveTool, useAddAction, useDfxUpload, useRenameModeStore, useIsComparing, useSelectedComment, useShortcutStore, useToggleView, useToolMode, useViewSceneStore } from "../../store/builder/store";
import useCameraModeStore, { usePlayButtonStore } from "../../store/ui/usePlayButtonStore";
import { detectModifierKeys } from "./detectModifierKeys";
import { useSelectedZoneStore } from "../../store/visualization/useZoneStore";
@@ -27,7 +27,7 @@ const KeyPressListener: React.FC = () => {
const { clearSelectedZone } = useSelectedZoneStore();
const { showShortcuts, setShowShortcuts } = useShortcutStore();
const { setWalkMode } = useCameraModeStore();
const { setIsVersionSaved } = useSaveVersion();
const { setIsComparing } = useIsComparing();
const { isLogListVisible, setIsLogListVisible } = useLogger();
const { hidePlayer, setHidePlayer } = usePlayerStore();
const { setViewSceneLabels } = useViewSceneStore();
@@ -177,7 +177,7 @@ const KeyPressListener: React.FC = () => {
setIsPlaying(false);
clearSelectedZone();
setShowShortcuts(false);
setIsVersionSaved(false);
setIsComparing(false);
clearComparisonProduct();
setIsLogListVisible(false);
setIsRenameMode(false);