separate the function exportpngheatmap

This commit is contained in:
2025-09-06 15:19:36 +05:30
parent 927c6dcfab
commit 5fd1c3bb89
3 changed files with 180 additions and 153 deletions

View File

@@ -3,12 +3,14 @@ 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 { exportHeatmapAsPNG } from "../functions/exportHeatmapAsPNG";
// Constants
const RADIUS = 0.0025;
const OPACITY = 0.8;
const GROWTH_RATE = 20.0;
// 🔹 React Component
const BakedHeatMap = () => {
const { bakedPoints } = useHeatMapStore();
const materialRef = useRef<THREE.ShaderMaterial>(null);
@@ -18,178 +20,109 @@ const BakedHeatMap = () => {
const height = CONSTANTS.gridConfig.size;
const width = CONSTANTS.gridConfig.size;
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 pointTexture = useMemo(() => {
if (bakedPoints.length === 0) return null;
const data = new Float32Array(bakedPoints.length * 4);
bakedPoints.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, bakedPoints.length, 1, THREE.RGBAFormat, THREE.FloatType);
texture.needsUpdate = true;
return texture;
}, [bakedPoints, width, height]);
const uniformsRef = useRef({
u_points: { value: null as THREE.DataTexture | null },
u_count: { value: 0 },
u_points: { value: pointTexture },
u_count: { value: bakedPoints.length },
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);
if (image) {
downloadImage(image, `${type}-heatmap.png`);
}
return { type, image };
});
console.log("Exported Heatmaps:", result);
return result;
}, [renderHeatmapToImage]);
const pointTexture = useMemo(() => createPointTexture(bakedPoints), [bakedPoints, createPointTexture]);
useEffect(() => {
uniformsRef.current.u_points.value = pointTexture;
uniformsRef.current.u_count.value = bakedPoints.length;
}, [pointTexture, bakedPoints.length]);
useEffect(() => {
if (meshRef.current) {
exportHeatmapAsPNG({
bakedPoints,
gl,
width: width,
height: height,
mesh: meshRef.current,
});
}
}, []);
return (
<>
<mesh ref={meshRef} rotation={[Math.PI / 2, 0, 0]} position={[0, 0.025, 0]}>
<planeGeometry args={[width, height]} />
<shaderMaterial
ref={materialRef}
transparent
depthWrite={false}
blending={THREE.AdditiveBlending}
uniforms={uniformsRef.current}
side={THREE.DoubleSide}
vertexShader={`
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`}
fragmentShader={`
uniform sampler2D u_points;
precision highp float;
uniform int u_count;
uniform float u_radius;
uniform float u_opacity;
uniform float u_growthRate;
varying vec2 vUv;
<></>
// <mesh ref={meshRef} rotation={[Math.PI / 2, 0, 0]} position={[0, 0.025, 0]}>
// <planeGeometry args={[width, height]} />
// <shaderMaterial
// ref={materialRef}
// transparent
// depthWrite={false}
// blending={THREE.AdditiveBlending}
// uniforms={uniformsRef.current}
// side={THREE.DoubleSide}
// vertexShader={`
// varying vec2 vUv;
// void main() {
// vUv = uv;
// gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
// }
// `}
// fragmentShader={`
// uniform sampler2D u_points;
// precision highp float;
// uniform int u_count;
// uniform float u_radius;
// uniform float u_opacity;
// uniform float u_growthRate;
// varying vec2 vUv;
float gauss(float dist, float radius) {
return exp(-pow(dist / radius, 2.0));
}
// float gauss(float dist, float radius) {
// return exp(-pow(dist / radius, 2.0));
// }
void main() {
float intensity = 0.0;
// void main() {
// float intensity = 0.0;
for (int i = 0; i < 10000; i++) {
if (i >= u_count) break;
float fi = float(i) + 0.5;
float u = fi / float(u_count);
// for (int i = 0; i < 10000; i++) {
// if (i >= 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;
// 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 d = distance(vUv, pos);
// intensity += strength * gauss(d, u_radius);
// }
float normalized = clamp(intensity / max(u_growthRate, 0.0001), 0.0, 1.0);
// 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);
}
// 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);
}
`}
/>
</mesh>
</>
// gl_FragColor = vec4(color, normalized * u_opacity);
// }
// `}
// />
// </mesh>
);
};

View File

@@ -0,0 +1,94 @@
import * as THREE from "three";
const RADIUS = 0.0025;
const OPACITY = 0.8;
const GROWTH_RATE = 20.0;
export function exportHeatmapAsPNG({ bakedPoints, gl, width, height, mesh }: { bakedPoints: any[]; gl: THREE.WebGLRenderer; width: number; height: number; mesh: THREE.Mesh }) {
const createPointTexture = (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;
};
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 renderHeatmapToImage = (type: string) => {
const filteredPoints = bakedPoints.filter((p) => p.type === type);
if (filteredPoints.length === 0) return null;
const pointTexture = createPointTexture(filteredPoints);
if (!pointTexture) return null;
const uniforms = {
u_points: { value: pointTexture },
u_count: { value: filteredPoints.length },
u_radius: { value: RADIUS },
u_opacity: { value: OPACITY },
u_growthRate: { value: GROWTH_RATE },
};
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(mesh);
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;
};
const types = ["human", "vehicle"];
const result = types.map((type) => {
const image = renderHeatmapToImage(type);
if (image) {
downloadImage(image, `${type}-heatmap.png`);
}
return { type, image };
});
console.log("Exported Heatmaps:", result);
return result;
}

View File

@@ -8,6 +8,6 @@ export const saveSimulationData = ({ key, data }: SimulationData) => {
};
export const getSimulationData = ({ key }: SimulationData) => {
const data = localStorage.getItem(key);
console.log("data: ", JSON.parse(data || "{}"));
// console.log("data: ", JSON.parse(data || "{}"));
};
export const clearSimulationData = ({ key, data }: SimulationData) => {};