diff --git a/app/package-lock.json b/app/package-lock.json
index b2ba539..04f7c7c 100644
--- a/app/package-lock.json
+++ b/app/package-lock.json
@@ -2029,7 +2029,7 @@
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jridgewell/trace-mapping": "0.3.9"
},
@@ -2041,7 +2041,7 @@
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
@@ -4192,6 +4192,26 @@
"url": "https://github.com/sponsors/gregberge"
}
},
+ "node_modules/@testing-library/dom": {
+ "version": "10.4.1",
+ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
+ "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/runtime": "^7.12.5",
+ "@types/aria-query": "^5.0.1",
+ "aria-query": "5.3.0",
+ "dom-accessibility-api": "^0.5.9",
+ "lz-string": "^1.5.0",
+ "picocolors": "1.1.1",
+ "pretty-format": "^27.0.2"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@testing-library/jest-dom": {
"version": "5.17.0",
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz",
@@ -4303,25 +4323,25 @@
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
- "dev": true
+ "devOptional": true
},
"node_modules/@tsconfig/node12": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
- "dev": true
+ "devOptional": true
},
"node_modules/@tsconfig/node14": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
- "dev": true
+ "devOptional": true
},
"node_modules/@tsconfig/node16": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
- "dev": true
+ "devOptional": true
},
"node_modules/@turf/along": {
"version": "7.2.0",
@@ -9093,7 +9113,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
- "dev": true
+ "devOptional": true
},
"node_modules/cross-env": {
"version": "7.0.3",
@@ -9970,7 +9990,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=0.3.1"
}
@@ -15354,7 +15374,7 @@
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
- "dev": true
+ "devOptional": true
},
"node_modules/makeerror": {
"version": "1.0.12",
@@ -20908,7 +20928,7 @@
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
@@ -20951,7 +20971,7 @@
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"acorn": "^8.11.0"
},
@@ -20963,7 +20983,7 @@
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
- "dev": true
+ "devOptional": true
},
"node_modules/tsconfig-paths": {
"version": "3.15.0",
@@ -21459,7 +21479,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
- "dev": true
+ "devOptional": true
},
"node_modules/v8-to-istanbul": {
"version": "8.1.1",
@@ -22518,7 +22538,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
- "dev": true,
+ "devOptional": true,
"engines": {
"node": ">=6"
}
diff --git a/app/src/components/layout/scenes/ComparisonScene.tsx b/app/src/components/layout/scenes/ComparisonScene.tsx
index 3756b08..4cbb543 100644
--- a/app/src/components/layout/scenes/ComparisonScene.tsx
+++ b/app/src/components/layout/scenes/ComparisonScene.tsx
@@ -1,4 +1,9 @@
-import { useCompareProductDataStore, useLoadingProgress, useIsComparing } from "../../../store/builder/store";
+import {
+ useCompareProductDataStore,
+ useLoadingProgress,
+ useIsComparing,
+ useCreateNewWindow,
+} from "../../../store/builder/store";
import { useSimulationState } from "../../../store/simulation/useSimulationStore";
import { usePlayButtonStore } from "../../../store/ui/usePlayButtonStore";
import { useEffect, useState } from "react";
@@ -11,7 +16,8 @@ import { useSimulationManager } from "../../../store/rough/useSimulationManagerS
import { useParams } from "react-router-dom";
import { validateSimulationDataApi } from "../../../services/simulation/comparison/validateSimulationDataApi";
import { calculateSimulationData } from "./functions/calculateSimulationData";
-
+import NewWindowScene from "../../ui/compareVersion/NewWindowScene";
+import Button from "../../ui/compareVersion/Button";
type AssetData = {
activeTime: number;
idleTime: number;
@@ -41,7 +47,11 @@ export interface CompareProduct {
//shiftsPerDay: number;
};
}
-export const createCompareProduct = (productUuid: string, productName: string, assets: AssetData[]): CompareProduct => ({
+export const createCompareProduct = (
+ productUuid: string,
+ productName: string,
+ assets: AssetData[]
+): CompareProduct => ({
productUuid,
productName,
simulationData: calculateSimulationData(assets),
@@ -61,8 +71,10 @@ function ComparisonScene() {
const { setCompareProductsData } = useCompareProductDataStore();
const [shouldShowComparisonResult, setShouldShowComparisonResult] = useState(false);
const { addSimulationRecord } = useSimulationManager();
-
- useEffect(() => {});
+ const { createNewWindow } = useCreateNewWindow();
+ useEffect(() => {
+ console.log("comparisonScene: ", comparisonScene);
+ }, [comparisonScene]);
const handleSelectVersion = (option: string) => {
const version = versionHistory.find((version) => version.versionName === option);
@@ -77,12 +89,22 @@ function ComparisonScene() {
echo.log(getData.message);
const getSimulate = getData?.data?.existingSimulatedData;
if (!getSimulate) return;
- if (!selectedVersion?.versionId || !projectId || getSimulate === undefined || !selectedProduct.productUuid) {
+ if (
+ !selectedVersion?.versionId ||
+ !projectId ||
+ getSimulate === undefined ||
+ !selectedProduct.productUuid
+ ) {
echo.warn("No prebacked Data found");
alert("Please run the simulation before comparing.");
return;
}
- addSimulationRecord(projectId, selectedVersion?.versionId || "", selectedProduct.productUuid || "", getSimulate.data);
+ addSimulationRecord(
+ projectId,
+ selectedVersion?.versionId || "",
+ selectedProduct.productUuid || "",
+ getSimulate.data
+ );
});
}
};
@@ -105,7 +127,12 @@ function ComparisonScene() {
echo.warn(getData.message);
const getSimulate = getData?.data?.existingSimulatedData;
if (!getSimulate) return;
- addSimulationRecord(projectId, selectedVersion?.versionId || "", product.productUuid || "", getSimulate.data);
+ addSimulationRecord(
+ projectId,
+ selectedVersion?.versionId || "",
+ product.productUuid || "",
+ getSimulate.data
+ );
});
setComparisonState(data);
}
@@ -113,12 +140,32 @@ function ComparisonScene() {
useEffect(() => {
if (mainScene && comparisonScene && selectedVersion) {
- const mainVersion = useSimulationManager.getState().getProductById(projectId, mainScene.version.versionUuid, mainScene.product.productUuid);
+ const mainVersion = useSimulationManager
+ .getState()
+ .getProductById(
+ projectId,
+ mainScene.version.versionUuid,
+ mainScene.product.productUuid
+ );
- const compareVersion = useSimulationManager.getState().getProductById(projectId, comparisonScene.version.versionUuid, comparisonScene.product.productUuid);
+ const compareVersion = useSimulationManager
+ .getState()
+ .getProductById(
+ projectId,
+ comparisonScene.version.versionUuid,
+ comparisonScene.product.productUuid
+ );
- const mainVompareversion = createCompareProduct(mainVersion?.productId ?? "", mainScene.product.productName, mainVersion?.simulateData || []);
- const compareProduct2 = createCompareProduct(compareVersion?.productId ?? "", comparisonScene.product.productName, compareVersion?.simulateData || []);
+ const mainVompareversion = createCompareProduct(
+ mainVersion?.productId ?? "",
+ mainScene.product.productName,
+ mainVersion?.simulateData || []
+ );
+ const compareProduct2 = createCompareProduct(
+ compareVersion?.productId ?? "",
+ comparisonScene.product.productName,
+ compareVersion?.simulateData || []
+ );
const comparedArray = [mainVompareversion, compareProduct2];
@@ -131,7 +178,14 @@ function ComparisonScene() {
} else {
setShouldShowComparisonResult(false);
}
- }, [mainScene, comparisonScene, selectedVersion, projectId, setCompareProductsData, simulationRecords]);
+ }, [
+ mainScene,
+ comparisonScene,
+ selectedVersion,
+ projectId,
+ setCompareProductsData,
+ simulationRecords,
+ ]);
return (
<>
@@ -154,7 +208,20 @@ function ComparisonScene() {
/>
)}
-
+ {selectedVersion?.versionId && (
+
+
+
+ )}
+ {}
+ {createNewWindow && }
{shouldShowComparisonResult && !loadingProgress && }
>
)}
diff --git a/app/src/components/templates/CreateNewWindow.tsx b/app/src/components/templates/CreateNewWindow.tsx
index bddc2ba..6794b3b 100644
--- a/app/src/components/templates/CreateNewWindow.tsx
+++ b/app/src/components/templates/CreateNewWindow.tsx
@@ -2,182 +2,382 @@ import React, { ReactNode, useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
type NewWindowProps = {
- children: ReactNode;
- title?: string;
- width?: number;
- height?: number;
- left?: number;
- top?: number;
- center?: boolean;
- features?: Partial<{
- toolbar: boolean;
- menubar: boolean;
- scrollbars: boolean;
- resizable: boolean;
- location: boolean;
- status: boolean;
- }>;
- onClose?: () => void;
- copyStyles?: boolean;
- noopener?: boolean;
- className?: string;
- theme?: string | null;
+ children: ReactNode;
+ title?: string;
+ width?: number;
+ height?: number;
+ left?: number;
+ top?: number;
+ center?: boolean;
+ features?: Partial<{
+ toolbar: boolean;
+ menubar: boolean;
+ scrollbars: boolean;
+ resizable: boolean;
+ location: boolean;
+ status: boolean;
+ }>;
+ onClose?: () => void;
+ copyStyles?: boolean;
+ noopener?: boolean;
+ className?: string;
+ theme?: string | null;
};
+// export const RenderInNewWindow: React.FC = ({
+// children,
+// title = "New Window",
+// width = 900,
+// height = 700,
+// left,
+// top,
+// center = true,
+// features,
+// onClose,
+// copyStyles = true,
+// noopener = true,
+// className,
+// theme = localStorage.getItem("theme") ?? "light",
+// }) => {
+// const [mounted, setMounted] = useState(false);
+// const childWindowRef = useRef(null);
+// const containerElRef = useRef(null);
+
+// useEffect(() => {
+// if (typeof window === "undefined") return;
+
+// const screenLeft = window.screenLeft ?? window.screenX ?? 0;
+// const screenTop = window.screenTop ?? window.screenY ?? 0;
+// const availWidth = window.outerWidth ?? window.innerWidth;
+// const availHeight = window.outerHeight ?? window.innerHeight;
+
+// const finalLeft =
+// center && availWidth ? Math.max(0, screenLeft + (availWidth - width) / 2) : left ?? 100;
+
+// const finalTop =
+// center && availHeight
+// ? Math.max(0, screenTop + (availHeight - height) / 2)
+// : top ?? 100;
+
+// const baseFeatures = [
+// `width=${Math.floor(width)}`,
+// `height=${Math.floor(height)}`,
+// `left=${Math.floor(finalLeft)}`,
+// `top=${Math.floor(finalTop)}`,
+// ];
+
+// const featureFlags = features ?? {
+// toolbar: false,
+// menubar: false,
+// scrollbars: true,
+// resizable: true,
+// location: false,
+// status: false,
+// };
+
+// Object.entries(featureFlags).forEach(([k, v]) =>
+// baseFeatures.push(`${k}=${v ? "yes" : "no"}`)
+// );
+
+// const newWin = window.open("", "_blank", baseFeatures.join(","));
+// if (!newWin) {
+// console.warn("Popup blocked or failed to open window.");
+// onClose?.();
+// return;
+// }
+
+// if (noopener) {
+// try {
+// newWin.opener = null;
+// } catch {}
+// }
+
+// newWin.document.open();
+// newWin.document.write(`
+//
+//
+//
+// ${title}
+//
+//
+//
+// `);
+// newWin.document.close();
+
+// if (copyStyles) {
+// const head = newWin.document.head;
+// Array.from(document.styleSheets).forEach((styleSheet) => {
+// try {
+// if ((styleSheet as CSSStyleSheet).cssRules) {
+// const newStyleEl = newWin.document.createElement("style");
+// const rules = Array.from((styleSheet as CSSStyleSheet).cssRules).map(
+// (r) => r.cssText
+// );
+// newStyleEl.appendChild(newWin.document.createTextNode(rules.join("\n")));
+// head.appendChild(newStyleEl);
+// }
+// } catch {
+// const ownerNode = styleSheet.ownerNode as HTMLElement | null;
+// if (ownerNode && ownerNode.tagName === "LINK") {
+// const link = ownerNode as HTMLLinkElement;
+// const newLink = newWin.document.createElement("link");
+// newLink.rel = link.rel;
+// newLink.href = link.href;
+// newLink.media = link.media;
+// head.appendChild(newLink);
+// }
+// }
+// });
+// }
+
+// const container = newWin.document.createElement("div");
+// if (className) container.className = className;
+// newWin.document.body.appendChild(container);
+
+// newWin.document.title = title;
+
+// // Handle child window close
+// const handleChildUnload = () => {
+// onClose?.();
+// };
+// newWin.addEventListener("beforeunload", handleChildUnload);
+
+// // ๐ Handle parent refresh/close โ auto close child
+// const handleParentUnload = () => {
+// try {
+// newWin.close();
+// } catch {}
+// };
+// window.addEventListener("beforeunload", handleParentUnload);
+
+// childWindowRef.current = newWin;
+// containerElRef.current = container;
+// setMounted(true);
+
+// return () => {
+// newWin.removeEventListener("beforeunload", handleChildUnload);
+// window.removeEventListener("beforeunload", handleParentUnload);
+// try {
+// newWin.close();
+// } catch {}
+// childWindowRef.current = null;
+// containerElRef.current = null;
+// };
+// // eslint-disable-next-line react-hooks/exhaustive-deps
+// }, []);
+
+// useEffect(() => {
+// const w = childWindowRef.current;
+// if (w && !w.closed) {
+// w.document.title = title;
+// }
+// }, [title]);
+
+// if (!mounted || !containerElRef.current) return null;
+
+// return createPortal(children, containerElRef.current);
+// };
export const RenderInNewWindow: React.FC = ({
- children,
- title = "New Window",
- width = 900,
- height = 700,
- left,
- top,
- center = true,
- features,
- onClose,
- copyStyles = true,
- noopener = true,
- className,
- theme = localStorage.getItem('theme') ?? 'light',
+ children,
+ title = "3D Viewer",
+ width = 900,
+ height = 700,
+ left,
+ top,
+ center = true,
+ features,
+ onClose,
+ copyStyles = true,
+ noopener = true,
+ className,
+ theme = localStorage.getItem("theme") ?? "light",
}) => {
- const [mounted, setMounted] = useState(false);
- const childWindowRef = useRef(null);
- const containerElRef = useRef(null);
+ const [mounted, setMounted] = useState(false);
+ const childWindowRef = useRef(null);
+ const containerElRef = useRef(null);
- useEffect(() => {
- if (typeof window === "undefined") return;
+ useEffect(() => {
+ if (typeof window === "undefined") return;
- const screenLeft = window.screenLeft ?? window.screenX ?? 0;
- const screenTop = window.screenTop ?? window.screenY ?? 0;
- const availWidth = window.outerWidth ?? window.innerWidth;
- const availHeight = window.outerHeight ?? window.innerHeight;
+ const screenLeft = window.screenLeft ?? window.screenX ?? 0;
+ const screenTop = window.screenTop ?? window.screenY ?? 0;
+ const availWidth = window.outerWidth ?? window.innerWidth;
+ const availHeight = window.outerHeight ?? window.innerHeight;
- const finalLeft =
- center && availWidth
- ? Math.max(0, screenLeft + (availWidth - width) / 2)
- : left ?? 100;
+ const finalLeft =
+ center && availWidth ? Math.max(0, screenLeft + (availWidth - width) / 2) : left ?? 100;
- const finalTop =
- center && availHeight
- ? Math.max(0, screenTop + (availHeight - height) / 2)
- : top ?? 100;
+ const finalTop =
+ center && availHeight
+ ? Math.max(0, screenTop + (availHeight - height) / 2)
+ : top ?? 100;
- const baseFeatures = [
- `width=${Math.floor(width)}`,
- `height=${Math.floor(height)}`,
- `left=${Math.floor(finalLeft)}`,
- `top=${Math.floor(finalTop)}`,
- ];
+ const baseFeatures = [
+ `width=${Math.floor(width)}`,
+ `height=${Math.floor(height)}`,
+ `left=${Math.floor(finalLeft)}`,
+ `top=${Math.floor(finalTop)}`,
+ ];
- const featureFlags = features ?? {
- toolbar: false,
- menubar: false,
- scrollbars: true,
- resizable: true,
- location: false,
- status: false,
- };
+ const featureFlags = features ?? {
+ toolbar: false,
+ menubar: false,
+ scrollbars: true,
+ resizable: true,
+ location: false,
+ status: false,
+ };
- Object.entries(featureFlags).forEach(([k, v]) =>
- baseFeatures.push(`${k}=${v ? "yes" : "no"}`)
- );
+ Object.entries(featureFlags).forEach(([k, v]) =>
+ baseFeatures.push(`${k}=${v ? "yes" : "no"}`)
+ );
- const newWin = window.open("", "_blank", baseFeatures.join(","));
- if (!newWin) {
- console.warn("Popup blocked or failed to open window.");
- onClose?.();
- return;
- }
+ const newWin = window.open("", "_blank", baseFeatures.join(","));
+ if (!newWin) {
+ console.warn("Popup blocked or failed to open window.");
+ onClose?.();
+ return;
+ }
- if (noopener) {
- try {
- newWin.opener = null;
- } catch {}
- }
+ if (noopener) {
+ try {
+ newWin.opener = null;
+ } catch {}
+ }
- newWin.document.open();
- newWin.document.write(`
+ newWin.document.open();
+ newWin.document.write(`
${title}
+
-
+
+
+
`);
- newWin.document.close();
+ newWin.document.close();
- if (copyStyles) {
- const head = newWin.document.head;
- Array.from(document.styleSheets).forEach((styleSheet) => {
- try {
- if ((styleSheet as CSSStyleSheet).cssRules) {
- const newStyleEl = newWin.document.createElement("style");
- const rules = Array.from(
- (styleSheet as CSSStyleSheet).cssRules
- ).map((r) => r.cssText);
- newStyleEl.appendChild(
- newWin.document.createTextNode(rules.join("\n"))
- );
- head.appendChild(newStyleEl);
- }
- } catch {
- const ownerNode = styleSheet.ownerNode as HTMLElement | null;
- if (ownerNode && ownerNode.tagName === "LINK") {
- const link = ownerNode as HTMLLinkElement;
- const newLink = newWin.document.createElement("link");
- newLink.rel = link.rel;
- newLink.href = link.href;
- newLink.media = link.media;
- head.appendChild(newLink);
- }
+ if (copyStyles) {
+ const head = newWin.document.head;
+ Array.from(document.styleSheets).forEach((styleSheet) => {
+ try {
+ if ((styleSheet as CSSStyleSheet).cssRules) {
+ const newStyleEl = newWin.document.createElement("style");
+ const rules = Array.from((styleSheet as CSSStyleSheet).cssRules).map(
+ (r) => r.cssText
+ );
+ newStyleEl.appendChild(newWin.document.createTextNode(rules.join("\n")));
+ head.appendChild(newStyleEl);
+ }
+ } catch {
+ const ownerNode = styleSheet.ownerNode as HTMLElement | null;
+ if (ownerNode && ownerNode.tagName === "LINK") {
+ const link = ownerNode as HTMLLinkElement;
+ const newLink = newWin.document.createElement("link");
+ newLink.rel = link.rel;
+ newLink.href = link.href;
+ newLink.media = link.media;
+ head.appendChild(newLink);
+ }
+ }
+ });
}
- });
- }
- const container = newWin.document.createElement("div");
- if (className) container.className = className;
- newWin.document.body.appendChild(container);
+ const container = newWin.document.getElementById(
+ "three-container"
+ ) as HTMLDivElement | null;
+ if (!container) return;
- newWin.document.title = title;
+ if (className) container.className = className;
- // Handle child window close
- const handleChildUnload = () => {
- onClose?.();
- };
- newWin.addEventListener("beforeunload", handleChildUnload);
+ newWin.document.title = title;
- // ๐ Handle parent refresh/close โ auto close child
- const handleParentUnload = () => {
- try {
- newWin.close();
- } catch {}
- };
- window.addEventListener("beforeunload", handleParentUnload);
+ // โ
CRITICAL FIX: Wait for window to be fully ready then trigger resize
+ const initializeWindow = () => {
+ // Force maximize handling
+ setTimeout(() => {
+ // Get actual window dimensions (might be maximized)
+ const actualWidth = newWin.innerWidth;
+ const actualHeight = newWin.innerHeight;
- childWindowRef.current = newWin;
- containerElRef.current = container;
- setMounted(true);
+ console.log("Window dimensions:", actualWidth, actualHeight);
- return () => {
- newWin.removeEventListener("beforeunload", handleChildUnload);
- window.removeEventListener("beforeunload", handleParentUnload);
- try {
- newWin.close();
- } catch {}
- childWindowRef.current = null;
- containerElRef.current = null;
- };
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
+ // Trigger resize event for Three.js
+ newWin.dispatchEvent(new Event("resize"));
- useEffect(() => {
- const w = childWindowRef.current;
- if (w && !w.closed) {
- w.document.title = title;
- }
- }, [title]);
+ // Additional safety: trigger again after a short delay
+ setTimeout(() => {
+ newWin.dispatchEvent(new Event("resize"));
+ }, 200);
+ }, 100);
+ };
- if (!mounted || !containerElRef.current) return null;
+ // Handle both load and focus events
+ newWin.addEventListener("load", initializeWindow);
+ newWin.addEventListener("focus", initializeWindow);
- return createPortal(children, containerElRef.current);
+ // Also initialize immediately if window is already loaded
+ if (newWin.document.readyState === "complete") {
+ initializeWindow();
+ } else {
+ newWin.addEventListener("DOMContentLoaded", initializeWindow);
+ }
+
+ // Handle child window close
+ const handleChildUnload = () => {
+ onClose?.();
+ };
+ newWin.addEventListener("beforeunload", handleChildUnload);
+
+ // Handle parent refresh/close โ auto close child
+ const handleParentUnload = () => {
+ try {
+ newWin.close();
+ } catch {}
+ };
+ window.addEventListener("beforeunload", handleParentUnload);
+
+ childWindowRef.current = newWin;
+ containerElRef.current = container;
+ setMounted(true);
+
+ return () => {
+ newWin.removeEventListener("load", initializeWindow);
+ newWin.removeEventListener("focus", initializeWindow);
+ newWin.removeEventListener("DOMContentLoaded", initializeWindow);
+ newWin.removeEventListener("beforeunload", handleChildUnload);
+ window.removeEventListener("beforeunload", handleParentUnload);
+ try {
+ newWin.close();
+ } catch {}
+ childWindowRef.current = null;
+ containerElRef.current = null;
+ };
+ }, []);
+
+ useEffect(() => {
+ const w = childWindowRef.current;
+ if (w && !w.closed) {
+ w.document.title = title;
+ }
+ }, [title]);
+
+ if (!mounted || !containerElRef.current) return null;
+
+ return createPortal(children, containerElRef.current);
};
diff --git a/app/src/components/templates/LoadingPage.tsx b/app/src/components/templates/LoadingPage.tsx
index 7e06262..d93ecba 100644
--- a/app/src/components/templates/LoadingPage.tsx
+++ b/app/src/components/templates/LoadingPage.tsx
@@ -9,14 +9,18 @@ interface LoadingPageProps {
}
const LoadingPage: React.FC = ({ progress }) => {
+ console.log('progress: ', progress);
const { projectName } = useProjectName();
const { comparisonScene } = useSimulationState();
const validatedProgress = Math.min(100, Math.max(0, progress));
+ console.log("comparisonScene: ", comparisonScene);
return (
-
+
{projectName}
diff --git a/app/src/components/ui/compareVersion/Button.tsx b/app/src/components/ui/compareVersion/Button.tsx
new file mode 100644
index 0000000..66e4124
--- /dev/null
+++ b/app/src/components/ui/compareVersion/Button.tsx
@@ -0,0 +1,47 @@
+import React from "react";
+import {
+ useCreateNewWindow,
+ useIsComparing,
+ useLoadingProgress,
+} from "../../../store/builder/store";
+import { useSimulationState } from "../../../store/simulation/useSimulationStore";
+
+const Button = () => {
+ const { isComparing, setIsComparing } = useIsComparing();
+ const { createNewWindow, setCreateNewWindow } = useCreateNewWindow();
+ const { setLoadingProgress } = useLoadingProgress();
+ const { clearComparisonState } = useSimulationState();
+
+ const handleExit = () => {
+ setIsComparing(false);
+ setCreateNewWindow(false);
+ setLoadingProgress(0);
+ clearComparisonState();
+ };
+
+ const handleOpenInNewWindow = () => {
+ // ๐งน Immediately reset any loading or scene state
+ setLoadingProgress(0);
+ setCreateNewWindow(true);
+ };
+
+ return (
+
+ {isComparing && }
+ {isComparing && !createNewWindow && (
+
+ )}
+
+ );
+};
+
+export default Button;
diff --git a/app/src/components/ui/compareVersion/CompareLayOut.tsx b/app/src/components/ui/compareVersion/CompareLayOut.tsx
index 23c6313..3b123f7 100644
--- a/app/src/components/ui/compareVersion/CompareLayOut.tsx
+++ b/app/src/components/ui/compareVersion/CompareLayOut.tsx
@@ -1,7 +1,11 @@
import { useParams } from "react-router-dom";
import React, { useState, useRef, useEffect, Suspense } from "react";
import { CompareLayoutIcon, LayoutIcon, ResizerIcon } from "../../icons/SimulationIcons";
-import { useLoadingProgress, useIsComparing } from "../../../store/builder/store";
+import {
+ useLoadingProgress,
+ useIsComparing,
+ useCreateNewWindow,
+} from "../../../store/builder/store";
import { useSimulationState } from "../../../store/simulation/useSimulationStore";
import { usePlayButtonStore } from "../../../store/ui/usePlayButtonStore";
import { useSceneContext } from "../../../modules/scene/sceneContext";
@@ -13,11 +17,18 @@ import useRestStates from "../../../hooks/useResetStates";
import { getVersionHistoryApi } from "../../../services/factoryBuilder/versionControl/getVersionHistoryApi";
import { validateSimulationDataApi } from "../../../services/simulation/comparison/validateSimulationDataApi";
+import Button from "./Button";
const CompareLayOut = () => {
const { clearComparisonState, comparisonScene, setComparisonState } = useSimulationState();
const { versionStore } = useSceneContext();
- const { versionHistory, selectedVersion, setSelectedVersion, clearSelectedVersion, setVersions } = versionStore();
+ const {
+ versionHistory,
+ selectedVersion,
+ setSelectedVersion,
+ clearSelectedVersion,
+ setVersions,
+ } = versionStore();
const { setLoadingProgress } = useLoadingProgress();
const [width, setWidth] = useState("50vw");
const [isResizing, setIsResizing] = useState(false);
@@ -30,6 +41,7 @@ const CompareLayOut = () => {
const { setIsPlaying } = usePlayButtonStore();
const { projectId } = useParams();
const { resetStates } = useRestStates();
+ const { createNewWindow } = useCreateNewWindow();
useEffect(() => {
return () => {
resetStates();
@@ -162,8 +174,8 @@ const CompareLayOut = () => {
setLoadingProgress(1);
const singleData = {
projectId: projectId,
- versionId: version.versionId,
- productUuid: data[0].productUuid,
+ versionId: version.versionId,
+ productUuid: data[0].productUuid,
};
validateSimulationDataApi(singleData).then((getData) => {
@@ -178,59 +190,77 @@ const CompareLayOut = () => {
};
return (
-
- {loadingProgress === 0 && selectedVersion?.versionId && (
-
- )}
-
- {selectedVersion?.versionId && (
-
-
-
-
-
- )}
-
- {width !== "0px" &&
- !selectedVersion?.versionId && ( // Show only if no layout selected
-
-
-
+ <>
+ {!createNewWindow && (
+
+ {loadingProgress === 0 && selectedVersion?.versionId && (
+
+ )}
+
+ {selectedVersion?.versionId && (
+
+
+
+
-
Choose Version to compare
-
+ )}
- {showLayoutDropdown && (
-
-
Versions
-
{}} />
-
- {versionHistory.map((version) => (
-
- ))}
+ {width !== "0px" &&
+ !selectedVersion?.versionId && ( // Show only if no layout selected
+
+
+
+
Choose Version to compare
+
+
+ {showLayoutDropdown && (
+
+
Versions
+
{}} />
+
+ {versionHistory.map((version) => (
+
+ ))}
+
+
+ )}
)}
-
- )}
- {/* Always show after layout is selected */}
-
-
+ {/* Always show after layout is selected */}
+
+
+ )}
+ >
);
};
diff --git a/app/src/components/ui/compareVersion/NewWindowScene.tsx b/app/src/components/ui/compareVersion/NewWindowScene.tsx
new file mode 100644
index 0000000..8e09060
--- /dev/null
+++ b/app/src/components/ui/compareVersion/NewWindowScene.tsx
@@ -0,0 +1,44 @@
+import React, { Suspense, useEffect, useState } from "react";
+import { RenderInNewWindow } from "../../templates/CreateNewWindow";
+import { useSceneContext } from "../../../modules/scene/sceneContext";
+import { useCreateNewWindow, useLoadingProgress } from "../../../store/builder/store";
+import Scene from "../../../modules/scene/scene";
+import ComparisonResult from "./ComparisonResult";
+import Button from "./Button";
+
+const NewWindowScene = () => {
+ const { versionStore } = useSceneContext();
+ const { selectedVersion } = versionStore();
+ const { setCreateNewWindow } = useCreateNewWindow();
+ const { loadingProgress } = useLoadingProgress();
+
+ return (
+ <>
+ {selectedVersion?.versionId && (
+
+
+ setCreateNewWindow(false)}
+ >
+
+
+
+
+ {!loadingProgress && }
+
+
+
+ )}
+ >
+ );
+};
+
+export default NewWindowScene;
diff --git a/app/src/components/ui/log/LogList.tsx b/app/src/components/ui/log/LogList.tsx
index b7b39dc..8f420d6 100644
--- a/app/src/components/ui/log/LogList.tsx
+++ b/app/src/components/ui/log/LogList.tsx
@@ -1,185 +1,164 @@
import React, { useEffect, useState } from "react";
-import {
- LogListIcon,
- CloseIcon,
- ExpandIcon2,
-} from "../../icons/ExportCommonIcons"; // Adjust path as needed
+import { LogListIcon, CloseIcon, ExpandIcon2 } from "../../icons/ExportCommonIcons"; // Adjust path as needed
import { LogEntry, useLogger } from "./LoggerContext";
import { GetLogIcon } from "../../footer/getLogIcons";
import { RenderInNewWindow } from "../../templates/CreateNewWindow";
// --- Logs Component ---
type LogsProps = {
- selectedTab: "all" | "info" | "warning" | "error";
- setSelectedTab: (tab: "all" | "info" | "warning" | "error") => void;
- clear: () => void;
- filteredLogs: LogEntry[];
- formatTimestamp: (date: Date) => string;
+ selectedTab: "all" | "info" | "warning" | "error";
+ setSelectedTab: (tab: "all" | "info" | "warning" | "error") => void;
+ clear: () => void;
+ filteredLogs: LogEntry[];
+ formatTimestamp: (date: Date) => string;
};
const Logs: React.FC
= ({
- selectedTab,
- setSelectedTab,
- clear,
- filteredLogs,
- formatTimestamp,
+ selectedTab,
+ setSelectedTab,
+ clear,
+ filteredLogs,
+ formatTimestamp,
}) => {
- return (
- <>
-
-
- {["all", "info", "warning", "error"].map((type) => (
-
- ))}
-
-
-
-
- {/* Log Entries */}
-
- {filteredLogs.length > 0 ? (
- filteredLogs.map((log) => (
-
-
{GetLogIcon(log.type)}
-
-
{log.message}
-
- {formatTimestamp(log.timestamp)}
+ return (
+ <>
+
+
+ {["all", "info", "warning", "error"].map((type) => (
+
+ ))}
-
+
- ))
- ) : (
-
- There are no logs to display at the moment.
-
- )}
-
- >
- );
+
+ {/* Log Entries */}
+
+ {filteredLogs.length > 0 ? (
+ filteredLogs.map((log) => (
+
+
{GetLogIcon(log.type)}
+
+
{log.message}
+
{formatTimestamp(log.timestamp)}
+
+
+ ))
+ ) : (
+
There are no logs to display at the moment.
+ )}
+
+ >
+ );
};
// --- LogList Component ---
const LogList: React.FC = () => {
- const {
- logs,
- clear,
- setIsLogListVisible,
- isLogListVisible,
- selectedTab,
- setSelectedTab,
- } = useLogger();
+ const { logs, clear, setIsLogListVisible, isLogListVisible, selectedTab, setSelectedTab } =
+ useLogger();
- const formatTimestamp = (date: Date) => new Date(date).toLocaleTimeString();
- const [open, setOpen] = useState(false);
+ const formatTimestamp = (date: Date) => new Date(date).toLocaleTimeString();
+ const [open, setOpen] = useState(false);
+ console.log('open: ', open);
+ const filteredLogs =
+ selectedTab === "all"
+ ? [...logs].reverse()
+ : [...logs].filter((log) => log.type === selectedTab).reverse();
- const filteredLogs =
- selectedTab === "all"
- ? [...logs].reverse()
- : [...logs].filter((log) => log.type === selectedTab).reverse();
+ useEffect(() => {
+ if (isLogListVisible && logs.length > 0) {
+ const lastLog = logs[logs.length - 1];
+ const validTypes = ["all", "info", "warning", "error"];
+ if (validTypes.includes(lastLog.type)) {
+ setSelectedTab(lastLog.type as any);
+ } else {
+ setSelectedTab("all");
+ }
+ }
+ // eslint-disable-next-line
+ }, [isLogListVisible]);
- useEffect(() => {
- if (isLogListVisible && logs.length > 0) {
- const lastLog = logs[logs.length - 1];
- const validTypes = ["all", "info", "warning", "error"];
- if (validTypes.includes(lastLog.type)) {
- setSelectedTab(lastLog.type as any);
- } else {
- setSelectedTab("all");
- }
- }
- // eslint-disable-next-line
- }, [isLogListVisible]);
+ return (
+ <>
+ {!open ? (
+
setIsLogListVisible(false)}>
+ {/* eslint-disable-next-line */}
+
{
+ e.stopPropagation();
+ }}
+ >
+
+
+
+
+
+
+
- return (
- <>
- {!open ? (
-
setIsLogListVisible(false)}
- >
- {/* eslint-disable-next-line */}
-
{
- e.stopPropagation();
- }}
- >
-
-
-
-
+ {/* Logs Section */}
+
+
-
Log List
-
-
-
-
-
-
-
- {/* Logs Section */}
-
-
-
- ) : (
-
{
- setOpen(false);
- setIsLogListVisible(false);
- }}
- >
-
-
-
-
- )}
- >
- );
+
+
+
+
+ )}
+ >
+ );
};
export default LogList;
+
\ No newline at end of file
diff --git a/app/src/modules/builder/asset/models/model/model.tsx b/app/src/modules/builder/asset/models/model/model.tsx
index 6fadd18..2a4834c 100644
--- a/app/src/modules/builder/asset/models/model/model.tsx
+++ b/app/src/modules/builder/asset/models/model/model.tsx
@@ -13,13 +13,17 @@ import { getAssetFieldApi } from "../../../../../services/factoryBuilder/asset/f
import { ModelAnimator } from "./animator/modelAnimator";
import { useModelEventHandlers } from "./eventHandlers/useModelEventHandlers";
-function Model({ asset, isRendered, loader }: Readonly<{ asset: Asset; isRendered: boolean; loader: GLTFLoader }>) {
+function Model({
+ asset,
+ isRendered,
+ loader,
+}: Readonly<{ asset: Asset; isRendered: boolean; loader: GLTFLoader }>) {
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
const savedTheme: string = localStorage.getItem("theme") || "light";
const { toolMode } = useToolMode();
const { toggleView } = useToggleView();
const { activeModule } = useModuleStore();
- const { assetStore } = useSceneContext();
+ const { assetStore, layout } = useSceneContext();
const { resetAnimation, hasSelectedAsset, updateSelectedAsset, selectedAssets } = assetStore();
const { setDeletableFloorAsset } = useBuilderStore();
const [gltfScene, setGltfScene] = useState
(null);
@@ -52,7 +56,12 @@ function Model({ asset, isRendered, loader }: Readonly<{ asset: Asset; isRendere
}, [activeModule, toolMode, selectedAssets]);
useEffect(() => {
- if (groupRef.current && selectedAssets.length === 1 && selectedAssets[0].userData.modelUuid === asset.modelUuid && hasSelectedAsset(asset.modelUuid)) {
+ if (
+ groupRef.current &&
+ selectedAssets.length === 1 &&
+ selectedAssets[0].userData.modelUuid === asset.modelUuid &&
+ hasSelectedAsset(asset.modelUuid)
+ ) {
updateSelectedAsset(groupRef.current);
}
}, [isRendered, selectedAssets, asset]);
@@ -130,7 +139,10 @@ function Model({ asset, isRendered, loader }: Readonly<{ asset: Asset; isRendere
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,
@@ -144,7 +156,8 @@ function Model({ asset, isRendered, loader }: Readonly<{ asset: Asset; isRendere
});
}, []);
- const { handleDblClick, handleClick, handlePointerOver, handlePointerOut, handleContextMenu } = useModelEventHandlers({ boundingBox, groupRef, asset });
+ const { handleDblClick, handleClick, handlePointerOver, handlePointerOut, handleContextMenu } =
+ useModelEventHandlers({ boundingBox, groupRef, asset });
return (
>
) : (
- <>{!hasSelectedAsset(asset.modelUuid) && }>
+ <>
+ {!hasSelectedAsset(asset.modelUuid) && (
+
+ )}
+ >
+ )}
+ {hasSelectedAsset(asset.modelUuid) && (
+
)}
- {hasSelectedAsset(asset.modelUuid) && }
>
)}
diff --git a/app/src/modules/builder/asset/models/models.tsx b/app/src/modules/builder/asset/models/models.tsx
index 588a617..2291aba 100644
--- a/app/src/modules/builder/asset/models/models.tsx
+++ b/app/src/modules/builder/asset/models/models.tsx
@@ -3,7 +3,11 @@ import { Group, Vector3 } from "three";
import { CameraControls } from "@react-three/drei";
import { GLTFLoader } from "three/examples/jsm/Addons";
import { useThree, useFrame } from "@react-three/fiber";
-import { useContextActionStore, useLimitDistance, useRenderDistance } from "../../../../store/builder/store";
+import {
+ useContextActionStore,
+ useLimitDistance,
+ useRenderDistance,
+} from "../../../../store/builder/store";
import { useSelectedAsset } from "../../../../store/simulation/useSimulationStore";
import { useSceneContext } from "../../../scene/sceneContext";
import useZoomMesh from "../../hooks/useZoomMesh";
@@ -11,12 +15,14 @@ import useCallBackOnKey from "../../../../utils/hooks/useCallBackOnKey";
import Model from "./model/model";
-const distanceWorker = new Worker(new URL("../../../../services/factoryBuilder/webWorkers/distanceWorker.js", import.meta.url));
+const distanceWorker = new Worker(
+ new URL("../../../../services/factoryBuilder/webWorkers/distanceWorker.js", import.meta.url)
+);
function Models({ loader }: { readonly loader: GLTFLoader }) {
const { controls, camera } = useThree();
const assetGroupRef = useRef(null);
- const { assetStore } = useSceneContext();
+ const { assetStore, layout } = useSceneContext();
const { assets, selectedAssets, getSelectedAssetUuids } = assetStore();
const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
const { contextAction, setContextAction } = useContextActionStore();
@@ -30,6 +36,14 @@ function Models({ loader }: { readonly loader: GLTFLoader }) {
// console.log(assets);
}, [assets]);
+ useEffect(() => {
+ const initialRenderMap: Record = {};
+ assets.forEach((asset) => {
+ initialRenderMap[asset.modelUuid] = true;
+ });
+ setRenderMap(initialRenderMap);
+ }, [assets.length]);
+
useEffect(() => {
if (contextAction === "focusAsset") {
zoomMeshes(getSelectedAssetUuids());
@@ -55,20 +69,51 @@ function Models({ loader }: { readonly loader: GLTFLoader }) {
return { ...prev, [modelUuid]: shouldRender };
});
};
- }, []);
+
+ return () => {
+ distanceWorker.terminate();
+ };
+ }, [distanceWorker, layout]);
useFrame(() => {
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,
+ // });
+
+ const assetVec = new Vector3(...asset.position);
+ const cameraVec = new Vector3(
+ cameraPos.current.x,
+ cameraPos.current.y,
+ cameraPos.current.z
+ );
+ const distance = assetVec.distanceTo(cameraVec);
+
+ if (limitDistance) {
+ if (!isRendered && distance <= renderDistance) {
+ setRenderMap((prev) => {
+ if (prev[asset.modelUuid] === true) return prev;
+ return { ...prev, [asset.modelUuid]: true };
+ });
+ } else if (isRendered && distance > renderDistance) {
+ setRenderMap((prev) => {
+ if (prev[asset.modelUuid] === false) return prev;
+ return { ...prev, [asset.modelUuid]: false };
+ });
+ }
+ } else if (!isRendered) {
+ setRenderMap((prev) => {
+ if (prev[asset.modelUuid] === true) return prev;
+ return { ...prev, [asset.modelUuid]: true };
+ });
+ }
}
});
@@ -88,7 +133,12 @@ function Models({ loader }: { readonly loader: GLTFLoader }) {
}}
>
{assets.map((asset) => (
-
+
))}
);
diff --git a/app/src/modules/scene/camera/syncCam.tsx b/app/src/modules/scene/camera/syncCam.tsx
index 2d4026b..dd2ebd3 100644
--- a/app/src/modules/scene/camera/syncCam.tsx
+++ b/app/src/modules/scene/camera/syncCam.tsx
@@ -2,13 +2,11 @@ import { Vector3 } from "three";
import { useFrame, useThree } from "@react-three/fiber";
import { CameraControls } from "@react-three/drei";
import { useSceneContext } from "../sceneContext";
-import { useIsComparing } from "../../../store/builder/store";
+import { useCreateNewWindow, useIsComparing } from "../../../store/builder/store";
import { useSceneStore } from "../../../store/scene/useSceneStore";
import { useSimulationState } from "../../../store/simulation/useSimulationStore";
import useModuleStore from "../../../store/ui/useModuleStore";
-import * as CONSTANTS from "../../../types/world/worldConstants";
-
function SyncCam() {
const { layout } = useSceneContext();
const { controls } = useThree();
@@ -16,19 +14,39 @@ function SyncCam() {
const { activeModule } = useModuleStore();
const { comparisonScene } = useSimulationState();
const { setCamera, camState } = useSceneStore();
+ const { windowRendered } = useCreateNewWindow();
+
+ function getControls() {
+ const position = (controls as CameraControls).getPosition(new Vector3());
+ const target = (controls as CameraControls).getTarget(new Vector3());
+ setCamera(position, target);
+ }
+
+ function setControls() {
+ (controls as CameraControls).setLookAt(
+ camState.position.x,
+ camState.position.y,
+ camState.position.z,
+ camState.target.x,
+ camState.target.y,
+ camState.target.z,
+ true
+ );
+ }
useFrame(() => {
- if (layout === "Comparison Layout" && controls && camState) {
- (controls as any).mouseButtons.left = CONSTANTS.controlsTransition.leftMouse;
- (controls as any).mouseButtons.right = CONSTANTS.controlsTransition.rightMouse;
- (controls as any).mouseButtons.wheel = CONSTANTS.controlsTransition.wheelMouse;
- (controls as any).mouseButtons.middle = CONSTANTS.controlsTransition.middleMouse;
- (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" && comparisonScene) {
- const position = (controls as CameraControls).getPosition(new Vector3());
- const target = (controls as CameraControls).getTarget(new Vector3());
- setCamera(position, target);
+ if (
+ controls &&
+ isComparing &&
+ activeModule === "simulation" &&
+ comparisonScene &&
+ camState
+ ) {
+ if (layout === windowRendered) {
+ getControls();
+ } else {
+ setControls();
+ }
}
});
diff --git a/app/src/modules/scene/scene.tsx b/app/src/modules/scene/scene.tsx
index fe59f21..c4939be 100644
--- a/app/src/modules/scene/scene.tsx
+++ b/app/src/modules/scene/scene.tsx
@@ -1,5 +1,5 @@
import { useEffect, useMemo } from "react";
-import { Canvas } from "@react-three/fiber";
+import { Canvas, useThree } from "@react-three/fiber";
import { KeyboardControls } from "@react-three/drei";
import { useSceneContext } from "./sceneContext";
@@ -11,13 +11,17 @@ import Collaboration from "../collaboration/collaboration";
import useModuleStore from "../../store/ui/useModuleStore";
import { useParams } from "react-router-dom";
import { getUserData } from "../../functions/getUserData";
-import { useLoadingProgress } from "../../store/builder/store";
+import { useCreateNewWindow, useLoadingProgress } from "../../store/builder/store";
import { useSocketStore } from "../../store/socket/useSocketStore";
import { Color, SRGBColorSpace } from "three";
import { compressImage } from "../../utils/compressImage";
import { ALPHA_ORG } from "../../pages/Dashboard";
-export default function Scene({ layout }: { readonly layout: "Main Layout" | "Comparison Layout" }) {
+export default function Scene({
+ layout,
+}: {
+ readonly layout: "Main Layout" | "Comparison Layout";
+}) {
const map = useMemo(
() => [
{ name: "forward", keys: ["ArrowUp", "w", "W"] },
@@ -35,11 +39,16 @@ export default function Scene({ layout }: { readonly layout: "Main Layout" | "Co
const { projectSocket } = useSocketStore();
const { activeModule } = useModuleStore();
const { loadingProgress } = useLoadingProgress();
+ const { setWindowRendered } = useCreateNewWindow();
useEffect(() => {
if (!projectId || loadingProgress !== 0) return;
const canvas = document.getElementById("sceneCanvas")?.getElementsByTagName("canvas")[0];
- if (!canvas || !(layoutType === "default" || (layoutType === "useCase" && organization === ALPHA_ORG))) return;
+ if (
+ !canvas ||
+ !(layoutType === "default" || (layoutType === "useCase" && organization === ALPHA_ORG))
+ )
+ return;
compressImage(canvas.toDataURL("image/png")).then((screenshotDataUrl) => {
const updateProjects = {
projectId,
@@ -64,11 +73,21 @@ export default function Scene({ layout }: { readonly layout: "Main Layout" | "Co
onContextMenu={(e) => {
e.preventDefault();
}}
+ resize={{ polyfill: ResizeObserver }}
+ style={{ width: "100vw", height: "100vh", background: "#202020" }}
performance={{ min: 0.9, max: 1.0 }}
onCreated={(e) => {
e.scene.background = layout === "Main Layout" ? null : new Color(0x19191d);
}}
- gl={{ outputColorSpace: SRGBColorSpace, powerPreference: "high-performance", antialias: true, preserveDrawingBuffer: true }}
+ gl={{
+ outputColorSpace: SRGBColorSpace,
+ powerPreference: "high-performance",
+ antialias: true,
+ preserveDrawingBuffer: true,
+ }}
+ onPointerEnter={() => {
+ setWindowRendered(layout);
+ }}
>
diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx
index 4665702..162b6ad 100644
--- a/app/src/pages/Project.tsx
+++ b/app/src/pages/Project.tsx
@@ -171,6 +171,7 @@ const Project: React.FC = () => {
+
{selectedUser && }
diff --git a/app/src/store/builder/store.ts b/app/src/store/builder/store.ts
index bb913c1..bda5a2b 100644
--- a/app/src/store/builder/store.ts
+++ b/app/src/store/builder/store.ts
@@ -138,7 +138,8 @@ export const useDrieTemp = create((set: any) => ({
export const useDrieUIValue = create((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) => ({
@@ -462,9 +463,18 @@ interface DecalStore {
// Create the Zustand store with types
export const useDecalStore = create((set) => ({
selectedSubCategory: "Safety",
- setSelectedSubCategory: (subCategory: string | null) => set({ selectedSubCategory: subCategory }),
+ setSelectedSubCategory: (subCategory: string | null) =>
+ set({ selectedSubCategory: subCategory }),
}));
+
export const comparsionMaterialData = create((set: any) => ({
materialData: [],
setMaterialData: (x: any) => set({ materialData: x }),
}));
+
+export const useCreateNewWindow = create((set: any) => ({
+ createNewWindow: false,
+ setCreateNewWindow: (x: any) => set({ createNewWindow: x }),
+ windowRendered: "",
+ setWindowRendered: (x: any) => set({ windowRendered: x }),
+}));