From cd391837caddb28f012790fd0e71230a4821c992 Mon Sep 17 00:00:00 2001 From: Poovizhi99 Date: Thu, 16 Oct 2025 11:34:47 +0530 Subject: [PATCH 1/2] solved heatmap preview bugs in comparsion --- .../SimulationDashboard/DashboardEditor.tsx | 1 - .../heatMapGenerator/baked/bakedHeatMap.tsx | 243 ------------------ .../components/heatMapGenerator/heatMap.tsx | 11 +- .../heatMapGenerator/heatMapRenderer.tsx | 68 +++++ .../heatMapGenerator/heatmapPreview.tsx | 10 +- .../realTime/realTimeHeatMap.tsx | 27 +- .../layout/scenes/ComparisonScene.tsx | 27 +- .../layout/scenes/functions/handleExit.ts | 29 +++ .../ui/compareVersion/CompareLayOut.tsx | 7 +- .../{Button.tsx => ComparisonToolbar.tsx} | 37 +-- .../ui/compareVersion/NewWindowScene.tsx | 7 +- app/src/modules/scene/scene.tsx | 123 +-------- app/src/modules/simulation/simulation.tsx | 4 +- .../functions/exportHeatmapAsPNG.ts | 0 .../functions/generateHeatmapOutput.ts | 7 +- .../simulator/simulationHandler.tsx | 4 +- .../simulation/simulator/simulator.tsx | 7 +- app/src/store/builder/store.ts | 4 +- .../store/rough/useSimulationManagerStore.ts | 69 +---- 19 files changed, 180 insertions(+), 505 deletions(-) delete mode 100644 app/src/components/heatMapGenerator/baked/bakedHeatMap.tsx create mode 100644 app/src/components/heatMapGenerator/heatMapRenderer.tsx create mode 100644 app/src/components/layout/scenes/functions/handleExit.ts rename app/src/components/ui/compareVersion/{Button.tsx => ComparisonToolbar.tsx} (56%) rename app/src/{components/heatMapGenerator => modules/simulation/simulator}/functions/exportHeatmapAsPNG.ts (100%) diff --git a/app/src/SimulationDashboard/DashboardEditor.tsx b/app/src/SimulationDashboard/DashboardEditor.tsx index 8f579ef..9493ccf 100644 --- a/app/src/SimulationDashboard/DashboardEditor.tsx +++ b/app/src/SimulationDashboard/DashboardEditor.tsx @@ -44,7 +44,6 @@ import { handleBlockDragStart } from "./functions/block/handleBlockDragStart"; const DashboardEditor: React.FC = () => { const [blocks, setBlocks] = useState([]); - console.log('blocks: ', blocks); const [editMode, setEditMode] = useState(false); const [selectedBlock, setSelectedBlock] = useState(null); const [selectedElement, setSelectedElement] = useState(null); diff --git a/app/src/components/heatMapGenerator/baked/bakedHeatMap.tsx b/app/src/components/heatMapGenerator/baked/bakedHeatMap.tsx deleted file mode 100644 index f980fde..0000000 --- a/app/src/components/heatMapGenerator/baked/bakedHeatMap.tsx +++ /dev/null @@ -1,243 +0,0 @@ -import * as THREE from "three"; -import { useEffect, useMemo, useRef, useCallback } from "react"; -import { useThree } from "@react-three/fiber"; -import { useHeatMapStore } from "../../../store/simulation/useHeatMapStore"; -import * as CONSTANTS from "../../../types/world/worldConstants"; -import { usePlayButtonStore } from "../../../store/ui/usePlayButtonStore"; -import { useSceneContext } from "../../../modules/scene/sceneContext"; - -const RADIUS = 0.0025; -const OPACITY = 0.8; -const GROWTH_RATE = 20.0; - -const BakedHeatMap = () => { - const { materialStore } = useSceneContext(); - const { bakedPoints } = useHeatMapStore(); - const materialRef = useRef(null); - const meshRef = useRef(null); - const { gl } = useThree(); - const { isPlaying } = usePlayButtonStore(); - const height = CONSTANTS.gridConfig.size; - const width = CONSTANTS.gridConfig.size; - const { materialHistory, materials } = materialStore(); - - const createPointTexture = useCallback( - (filteredPoints: typeof bakedPoints) => { - if (filteredPoints.length === 0) return null; - - const data = new Float32Array(filteredPoints.length * 4); - filteredPoints.forEach((p, i) => { - const index = i * 4; - data[index] = (p.points.x + width / 2) / width; - data[index + 1] = (p.points.y + height / 2) / height; - data[index + 2] = 0.3; - data[index + 3] = 0.0; - }); - - const texture = new THREE.DataTexture( - data, - filteredPoints.length, - 1, - THREE.RGBAFormat, - THREE.FloatType - ); - texture.needsUpdate = true; - return texture; - }, - [width, height] - ); - - const uniformsRef = useRef({ - u_points: { value: null as THREE.DataTexture | null }, - u_count: { value: 0 }, - u_radius: { value: RADIUS }, - u_opacity: { value: OPACITY }, - u_growthRate: { value: GROWTH_RATE }, - }); - - const renderHeatmapToImage = useCallback( - (type: string) => { - if (!meshRef.current) return null; - - const filteredPoints = bakedPoints.filter((p) => p.type === type); - if (filteredPoints.length === 0) return null; - - const pointTexture = createPointTexture(filteredPoints); - if (!pointTexture) return null; - - uniformsRef.current.u_points.value = pointTexture; - uniformsRef.current.u_count.value = filteredPoints.length; - - const exportCamera = new THREE.OrthographicCamera( - width / -2, - width / 2, - height / 2, - height / -2, - 0.1, - 10 - ); - exportCamera.position.set(0, 1, 0); - exportCamera.lookAt(0, 0, 0); - - const renderTarget = new THREE.WebGLRenderTarget(1024, 1024, { - format: THREE.RGBAFormat, - type: THREE.UnsignedByteType, - }); - - const tempScene = new THREE.Scene(); - tempScene.add(meshRef.current); - - gl.setRenderTarget(renderTarget); - gl.render(tempScene, exportCamera); - gl.setRenderTarget(null); - - const pixels = new Uint8Array(1024 * 1024 * 4); - gl.readRenderTargetPixels(renderTarget, 0, 0, 1024, 1024, pixels); - - const canvas = document.createElement("canvas"); - canvas.width = 1024; - canvas.height = 1024; - const ctx = canvas.getContext("2d"); - - if (ctx) { - const imageData = ctx.createImageData(1024, 1024); - imageData.data.set(pixels); - ctx.putImageData(imageData, 0, 0); - return canvas.toDataURL("image/png"); - } - - return null; - }, - [gl, width, height, bakedPoints, createPointTexture] - ); - - // const downloadImage = (base64: string, filename: string) => { - // const link = document.createElement("a"); - // link.href = base64; - // link.download = filename; - // document.body.appendChild(link); - // link.click(); - // document.body.removeChild(link); - // }; - - const exportHeatmapAsPNG = useCallback(() => { - const types = ["human", "vehicle"]; - - const result = types.map((type) => { - const image = renderHeatmapToImage(type); - // console.log("image: ", type, image); - if (image) { - // downloadImage(image, `${type}-heatmap.png`); - } - return { type, image }; - }); - - // console.log("Exported Heatmaps:", result); - return result; - }, [renderHeatmapToImage]); - - useEffect(() => { - if (materials.length === 0 && materialHistory.length >= 0) { - // console.log("SimulationCompleted"); - const getImage = exportHeatmapAsPNG(); - // console.log("getImage: ", getImage); - } - }, [isPlaying, materials, materialHistory]); - - const pointTexture = useMemo( - () => createPointTexture(bakedPoints), - [bakedPoints, createPointTexture] - ); - - useEffect(() => { - uniformsRef.current.u_points.value = pointTexture; - uniformsRef.current.u_count.value = bakedPoints.length; - }, [pointTexture, bakedPoints.length]); - - return ( - <> - - - = u_count) break; - float fi = float(i) + 0.5; - float u = fi / float(u_count); - - vec4 point = texture2D(u_points, vec2(u, 0.5)); - vec2 pos = point.rg; - float strength = point.b; - - float d = distance(vUv, pos); - intensity += strength * gauss(d, u_radius); - } - - float normalized = clamp(intensity / max(u_growthRate, 0.0001), 0.0, 1.0); - - vec3 color = vec3(0.0); - if (normalized < 0.33) { - color = mix(vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 0.0), normalized / 0.33); - } else if (normalized < 0.66) { - color = mix(vec3(0.0, 1.0, 0.0), vec3(1.0, 1.0, 0.0), (normalized - 0.33) / 0.33); - } else { - color = mix(vec3(1.0, 1.0, 0.0), vec3(1.0, 0.0, 0.0), (normalized - 0.66) / 0.34); - } - - gl_FragColor = vec4(color, normalized * u_opacity); - } - `} - /> - - - {/* - - */} - - ); -}; - -export default BakedHeatMap; diff --git a/app/src/components/heatMapGenerator/heatMap.tsx b/app/src/components/heatMapGenerator/heatMap.tsx index 83b987a..89fad23 100644 --- a/app/src/components/heatMapGenerator/heatMap.tsx +++ b/app/src/components/heatMapGenerator/heatMap.tsx @@ -1,14 +1,11 @@ import RealTimeHeatMap from "./realTime/realTimeHeatMap"; -import BakedHeatMap from "./baked/bakedHeatMap"; +import { useIsComparing } from "../../store/builder/store"; +import HeatMapRenderer from "./heatMapRenderer"; function HeatMap() { - return ( - <> - + const { isComparing } = useIsComparing(); - - - ); + return <>{isComparing ? : }; } export default HeatMap; diff --git a/app/src/components/heatMapGenerator/heatMapRenderer.tsx b/app/src/components/heatMapGenerator/heatMapRenderer.tsx new file mode 100644 index 0000000..2e6925f --- /dev/null +++ b/app/src/components/heatMapGenerator/heatMapRenderer.tsx @@ -0,0 +1,68 @@ +import React, { useMemo } from "react"; +import { useSceneContext } from "../../modules/scene/sceneContext"; +import { useSimulationManager } from "../../store/rough/useSimulationManagerStore"; +import { useSimulationState } from "../../store/simulation/useSimulationStore"; +import HeatmapPreview from "./heatmapPreview"; + +const HeatMapRenderer = () => { + const { versionStore, layout } = useSceneContext(); + const { selectedVersion } = versionStore(); + const { simulationRecords } = useSimulationManager(); + const { mainScene, comparisonScene } = useSimulationState(); + + const { mainSceneHeatmaps, comparisonSceneHeatmaps } = useMemo(() => { + const getHeatmaps = (scene: any) => { + const heatmaps: Array<{ image: string | Blob; type: string }> = []; + if (!scene) return heatmaps; + + simulationRecords.forEach((project) => + project.versions.forEach((version) => + version.products.forEach((product) => + product.simulateData.forEach((simulateDataItem) => { + const isTargetScene = + product.productId === scene.product.productUuid && + version.versionId === scene.version.versionUuid && + selectedVersion?.versionId; + if (!isTargetScene) return; + + product.heatMaps?.forEach((heatMap) => { + if (heatMap.type !== simulateDataItem.type) return; + const img = heatMap.image; + + if (typeof img === "string") { + if (/\.(png|jpg)$/i.test(img)) { + heatmaps.push({ image: img, type: heatMap.type }); + } + } else if (img instanceof Blob) { + heatmaps.push({ image: img, type: heatMap.type }); + } + }); + }) + ) + ) + ); + + return heatmaps; + }; + + return { + mainSceneHeatmaps: getHeatmaps(mainScene), + comparisonSceneHeatmaps: getHeatmaps(comparisonScene), + }; + }, [simulationRecords, mainScene, comparisonScene, selectedVersion]); + + return ( + <> + {layout === "Main Layout" && + mainSceneHeatmaps.map((heatMap, idx) => ( + + ))} + {layout === "Comparison Layout" && + comparisonSceneHeatmaps.map((heatMap, idx) => ( + + ))} + + ); +}; + +export default HeatMapRenderer; diff --git a/app/src/components/heatMapGenerator/heatmapPreview.tsx b/app/src/components/heatMapGenerator/heatmapPreview.tsx index 4aec0f4..442b67e 100644 --- a/app/src/components/heatMapGenerator/heatmapPreview.tsx +++ b/app/src/components/heatMapGenerator/heatmapPreview.tsx @@ -1,6 +1,7 @@ import { useState, useEffect } from "react"; import { TextureLoader, Texture, DoubleSide } from "three"; import * as CONSTANTS from "../../types/world/worldConstants"; +import { useHeatmapTypeStore } from "../../store/builder/store"; interface HeatmapPreviewProps { image: string | Blob | null; @@ -9,6 +10,7 @@ interface HeatmapPreviewProps { const HeatmapPreview: React.FC = ({ image, type }) => { const [texture, setTexture] = useState(null); + const { selectedTypes } = useHeatmapTypeStore(); const width = CONSTANTS.gridConfig.size; const height = CONSTANTS.gridConfig.size; @@ -64,16 +66,16 @@ const HeatmapPreview: React.FC = ({ image, type }) => { return ( <> - {type === "human" && ( + {type === "human" && selectedTypes.human && ( - + )} - {type === "vehicle" && ( + {type === "vehicle" && selectedTypes.vehicle && ( - + )} diff --git a/app/src/components/heatMapGenerator/realTime/realTimeHeatMap.tsx b/app/src/components/heatMapGenerator/realTime/realTimeHeatMap.tsx index c002181..255e8f6 100644 --- a/app/src/components/heatMapGenerator/realTime/realTimeHeatMap.tsx +++ b/app/src/components/heatMapGenerator/realTime/realTimeHeatMap.tsx @@ -33,16 +33,8 @@ const RealTimeHeatMap = () => { const debugModeMap = { solid: 0, grayscale: 1, normal: 2 } as const; const { productStore } = useSceneContext(); const { getProductById, products, selectedProduct } = productStore(); - const { - bakedPoints, - hasHuman, - hasVehicle, - monitoringHuman, - monitoringVehicle, - addMonitoringHuman, - addMonitoringVehicle, - addBakedPoint, - } = useHeatMapStore(); + const { monitoringHuman, monitoringVehicle, clearBakedPoints, addBakedPoint } = + useHeatMapStore(); const { isPlaying } = usePlayButtonStore(); const { isReset } = useResetButtonStore(); const { isPaused } = usePauseButtonStore(); @@ -65,13 +57,14 @@ const RealTimeHeatMap = () => { u_growthRate: { value: GROWTH_TIME_MULTIPLIER }, }); - // useEffect(() => { - // if (isReset || !isPlaying) { - // setPoints([]); - // lastFrameTime.current = null; - // lastUpdateTime.current = 0; - // } - // }, [isReset, isPlaying]); + useEffect(() => { + if (isReset || !isPlaying) { + clearBakedPoints(); + setPoints([]); + lastFrameTime.current = null; + lastUpdateTime.current = 0; + } + }, [isReset, isPlaying]); // Added human or vehicle // useEffect(() => { diff --git a/app/src/components/layout/scenes/ComparisonScene.tsx b/app/src/components/layout/scenes/ComparisonScene.tsx index c9e4c6d..31aa99c 100644 --- a/app/src/components/layout/scenes/ComparisonScene.tsx +++ b/app/src/components/layout/scenes/ComparisonScene.tsx @@ -3,6 +3,8 @@ import { useLoadingProgress, useIsComparing, useCreateNewWindow, + useLimitDistance, + useRenderDistance, } from "../../../store/builder/store"; import { useSimulationState } from "../../../store/simulation/useSimulationStore"; import { usePlayButtonStore } from "../../../store/ui/usePlayButtonStore"; @@ -17,7 +19,8 @@ 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"; +import ComparisonToolbar from "../../ui/compareVersion/ComparisonToolbar"; +import { findEnvironment } from "../../../services/factoryBuilder/environment/findEnvironment"; type AssetData = { activeTime: number; idleTime: number; @@ -56,7 +59,7 @@ function ComparisonScene() { const { productStore, versionStore } = useSceneContext(); const { versionHistory, selectedVersion, setSelectedVersion } = versionStore(); const { products, selectedProduct } = productStore(); - const { isComparing } = useIsComparing(); + const { isComparing, setIsComparing } = useIsComparing(); const { activeModule } = useModuleStore(); const { mainScene, comparisonScene, setComparisonState } = useSimulationState(); const { loadingProgress } = useLoadingProgress(); @@ -65,8 +68,10 @@ function ComparisonScene() { const { setCompareProductsData } = useCompareProductDataStore(); const [shouldShowComparisonResult, setShouldShowComparisonResult] = useState(false); const { addSimulationRecord } = useSimulationManager(); - const { createNewWindow } = useCreateNewWindow(); - + const { createNewWindow, setCreateNewWindow } = useCreateNewWindow(); + const { clearComparisonState } = useSimulationState(); + const { setRenderDistance } = useRenderDistance(); + const { setLimitDistance } = useLimitDistance(); const handleSelectVersion = (option: string) => { const version = versionHistory.find((version) => version.versionName === option); if (version) { @@ -178,6 +183,17 @@ function ComparisonScene() { simulationRecords, ]); + const handleExit = () => { + setIsComparing(false); + setCreateNewWindow(false); + clearComparisonState(); + if (!projectId) return; + findEnvironment(projectId).then((data) => { + if (!data) return; + setRenderDistance(data.renderDistance); + setLimitDistance(data.limitDistance); + }); + }; return ( <> {isComparing && activeModule === "simulation" && selectedProduct && ( @@ -208,9 +224,10 @@ function ComparisonScene() { zIndex: 10, }} > - {width !== "0px" && !selectedVersion?.versionId && ( // Show only if no layout selected
diff --git a/app/src/components/ui/compareVersion/Button.tsx b/app/src/components/ui/compareVersion/ComparisonToolbar.tsx similarity index 56% rename from app/src/components/ui/compareVersion/Button.tsx rename to app/src/components/ui/compareVersion/ComparisonToolbar.tsx index fde4b42..dd9722c 100644 --- a/app/src/components/ui/compareVersion/Button.tsx +++ b/app/src/components/ui/compareVersion/ComparisonToolbar.tsx @@ -1,35 +1,16 @@ -import React, { useState } from "react"; +import { useParams } from "react-router-dom"; import { useCreateNewWindow, useHeatmapTypeStore, useIsComparing, - useLimitDistance, - useRenderDistance, } from "../../../store/builder/store"; -import { useSimulationState } from "../../../store/simulation/useSimulationStore"; -import { useParams } from "react-router-dom"; -import { findEnvironment } from "../../../services/factoryBuilder/environment/findEnvironment"; +import { handleExit } from "../../layout/scenes/functions/handleExit"; -const Button = () => { - const { isComparing, setIsComparing } = useIsComparing(); +const ComparisonToolbar = () => { + const { isComparing } = useIsComparing(); const { createNewWindow, setCreateNewWindow } = useCreateNewWindow(); - const { clearComparisonState } = useSimulationState(); - const { projectId } = useParams(); - const { setRenderDistance } = useRenderDistance(); - const { setLimitDistance } = useLimitDistance(); const { selectedTypes, toggleType } = useHeatmapTypeStore(); - - const handleExit = () => { - setIsComparing(false); - setCreateNewWindow(false); - clearComparisonState(); - if (!projectId) return; - findEnvironment(projectId).then((data) => { - if (!data) return; - setRenderDistance(data.renderDistance); - setLimitDistance(data.limitDistance); - }); - }; + const { projectId } = useParams(); const handleOpenInNewWindow = () => { setCreateNewWindow(true); @@ -46,7 +27,7 @@ const Button = () => { gap: "10px", }} > - {isComparing && } + {isComparing && } {isComparing && !createNewWindow && ( )} @@ -57,7 +38,7 @@ const Button = () => { checked={selectedTypes.vehicle} onChange={() => toggleType("vehicle")} /> - Vehicle +

Vehicle

); }; -export default Button; +export default ComparisonToolbar; diff --git a/app/src/components/ui/compareVersion/NewWindowScene.tsx b/app/src/components/ui/compareVersion/NewWindowScene.tsx index a889c39..899b151 100644 --- a/app/src/components/ui/compareVersion/NewWindowScene.tsx +++ b/app/src/components/ui/compareVersion/NewWindowScene.tsx @@ -1,10 +1,10 @@ import { Suspense } from "react"; +import { useCreateNewWindow, useLoadingProgress } from "../../../store/builder/store"; 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"; +import ComparisonToolbar from "./ComparisonToolbar"; import LoadingPage from "../../templates/LoadingPage"; const NewWindowScene = () => { @@ -22,7 +22,6 @@ const NewWindowScene = () => { title="3D Viewer" onClose={() => setCreateNewWindow(false)} > - {/* Wait a tick to access child window */} {loadingProgress > 0 && ( { zIndex: 1000, }} > -