diff --git a/app/src/components/heatMapGenerator/baked/bakedHeatMap.tsx b/app/src/components/heatMapGenerator/baked/bakedHeatMap.tsx
index ab745ba..a64307f 100644
--- a/app/src/components/heatMapGenerator/baked/bakedHeatMap.tsx
+++ b/app/src/components/heatMapGenerator/baked/bakedHeatMap.tsx
@@ -18,9 +18,6 @@ const BakedHeatMap = () => {
const height = CONSTANTS.gridConfig.size;
const width = CONSTANTS.gridConfig.size;
- /**
- * Helper: Create a DataTexture from filtered baked points
- */
const createPointTexture = useCallback(
(filteredPoints: typeof bakedPoints) => {
if (filteredPoints.length === 0) return null;
@@ -30,8 +27,8 @@ const BakedHeatMap = () => {
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; // heat strength
- data[index + 3] = 0.0; // unused
+ data[index + 2] = 0.3;
+ data[index + 3] = 0.0;
});
const texture = new THREE.DataTexture(
@@ -55,25 +52,20 @@ const BakedHeatMap = () => {
u_growthRate: { value: GROWTH_RATE },
});
- /**
- * Render the heatmap to a base64 PNG for a specific type (human/vehicle)
- */
+
const renderHeatmapToImage = useCallback(
(type: string) => {
if (!meshRef.current) return null;
- // Filter points by type first
const filteredPoints = bakedPoints.filter((p) => p.type === type);
if (filteredPoints.length === 0) return null;
- // Create texture for these points
const pointTexture = createPointTexture(filteredPoints);
if (!pointTexture) return null;
uniformsRef.current.u_points.value = pointTexture;
uniformsRef.current.u_count.value = filteredPoints.length;
- // Temporary top-down orthographic camera
const exportCamera = new THREE.OrthographicCamera(
width / -2,
width / 2,
@@ -85,26 +77,23 @@ const BakedHeatMap = () => {
exportCamera.position.set(0, 1, 0);
exportCamera.lookAt(0, 0, 0);
- // Offscreen render target
+
const renderTarget = new THREE.WebGLRenderTarget(1024, 1024, {
format: THREE.RGBAFormat,
type: THREE.UnsignedByteType,
});
- // Temporary scene with only this mesh
const tempScene = new THREE.Scene();
tempScene.add(meshRef.current);
- // Render to texture
gl.setRenderTarget(renderTarget);
gl.render(tempScene, exportCamera);
gl.setRenderTarget(null);
- // Read pixels
+
const pixels = new Uint8Array(1024 * 1024 * 4);
gl.readRenderTargetPixels(renderTarget, 0, 0, 1024, 1024, pixels);
- // Convert pixels to base64 PNG
const canvas = document.createElement("canvas");
canvas.width = 1024;
canvas.height = 1024;
@@ -116,30 +105,38 @@ const BakedHeatMap = () => {
ctx.putImageData(imageData, 0, 0);
return canvas.toDataURL("image/png");
}
+
return null;
},
[gl, width, height, bakedPoints, createPointTexture]
);
- /**
- * Export both human and vehicle heatmaps and log them
- */
+
+ 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);
- return {
- type,
- image,
- };
+ if (image) {
+ downloadImage(image, `${type}-heatmap.png`);
+ }
+ return { type, image };
});
console.log("Exported Heatmaps:", result);
return result;
}, [renderHeatmapToImage]);
- // Create default texture for initial rendering
+
const pointTexture = useMemo(() => createPointTexture(bakedPoints), [bakedPoints, createPointTexture]);
useEffect(() => {
@@ -149,6 +146,7 @@ const BakedHeatMap = () => {
return (
<>
+
{
/>
- {/* Button to export */}