added heatmap
This commit is contained in:
203
app/src/components/HeatMap/HeatMap.tsx
Normal file
203
app/src/components/HeatMap/HeatMap.tsx
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
import * as THREE from "three";
|
||||||
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
import { useFrame, useThree } from "@react-three/fiber";
|
||||||
|
import { useProductContext } from "../../modules/simulation/products/productContext";
|
||||||
|
import { useSceneContext } from "../../modules/scene/sceneContext";
|
||||||
|
import * as CONSTANTS from "../../types/world/worldConstants";
|
||||||
|
import { determineExecutionMachineSequences } from "../../modules/simulation/simulator/functions/determineExecutionMachineSequences";
|
||||||
|
|
||||||
|
const MAX_POINTS = 100;
|
||||||
|
|
||||||
|
const HeatMap = () => {
|
||||||
|
const isUsingBBOX = false;
|
||||||
|
const height = CONSTANTS.gridConfig.size;
|
||||||
|
const width = CONSTANTS.gridConfig.size;
|
||||||
|
const radius = 0.005;
|
||||||
|
const opacity = 0.8; const debugMode: "solid" | "grayscale" | "normal" = "normal";
|
||||||
|
const debugModeMap = { solid: 0, grayscale: 1, normal: 2, } as const;
|
||||||
|
const { productStore } = useSceneContext();
|
||||||
|
const { getProductById, products } = productStore();
|
||||||
|
const { selectedProductStore } = useProductContext();
|
||||||
|
const { selectedProduct } = selectedProductStore();
|
||||||
|
const { scene } = useThree();
|
||||||
|
const [events, setEvents] = useState<EventsSchema[]>([]);
|
||||||
|
const [points, setPoints] = useState<{ x: number; y: number; value: number; }[]>([]);
|
||||||
|
const materialRef = useRef<THREE.ShaderMaterial>(null);
|
||||||
|
const uniformsRef = useRef({
|
||||||
|
u_points: { value: null as THREE.DataTexture | null },
|
||||||
|
u_count: { value: 0 },
|
||||||
|
u_radius: { value: radius },
|
||||||
|
u_opacity: { value: opacity },
|
||||||
|
u_debugMode: { value: debugModeMap[debugMode] },
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const selectedProductData = getProductById(selectedProduct.productUuid);
|
||||||
|
const events: EventsSchema[] = [];
|
||||||
|
|
||||||
|
if (selectedProductData) {
|
||||||
|
determineExecutionMachineSequences([selectedProductData]).then(
|
||||||
|
(sequences) => {
|
||||||
|
sequences.forEach((sequence) => {
|
||||||
|
sequence.forEach((event) => {
|
||||||
|
if (event.type === 'human' || event.type === 'vehicle') {
|
||||||
|
events.push(event);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
setEvents(events);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [selectedProduct, products])
|
||||||
|
|
||||||
|
useFrame(() => {
|
||||||
|
if (!materialRef.current || !scene) return;
|
||||||
|
|
||||||
|
const heatmapPoints: { x: number; y: number; value: number }[] = [];
|
||||||
|
|
||||||
|
events.forEach((event) => {
|
||||||
|
const model = scene.getObjectByProperty('uuid', event.modelUuid) as THREE.Object3D;
|
||||||
|
if (!model) return;
|
||||||
|
|
||||||
|
if (isUsingBBOX) {
|
||||||
|
const box = new THREE.Box3().setFromObject(model);
|
||||||
|
|
||||||
|
const { min, max } = box;
|
||||||
|
|
||||||
|
const corners = [
|
||||||
|
new THREE.Vector3(min.x, 0, min.z),
|
||||||
|
new THREE.Vector3(min.x, 0, max.z),
|
||||||
|
new THREE.Vector3(max.x, 0, min.z),
|
||||||
|
new THREE.Vector3(max.x, 0, max.z),
|
||||||
|
];
|
||||||
|
|
||||||
|
corners.forEach((corner) => {
|
||||||
|
heatmapPoints.push({
|
||||||
|
x: corner.x,
|
||||||
|
y: corner.z,
|
||||||
|
value: 1.0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
heatmapPoints.push({
|
||||||
|
x: model.position.x,
|
||||||
|
y: model.position.z,
|
||||||
|
value: 1.0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setPoints(heatmapPoints);
|
||||||
|
});
|
||||||
|
|
||||||
|
const pointTexture = useMemo(() => {
|
||||||
|
if (points.length === 0) return null;
|
||||||
|
const data = new Float32Array(MAX_POINTS * 4);
|
||||||
|
|
||||||
|
points.forEach((p, i) => {
|
||||||
|
const index = i * 4;
|
||||||
|
data[index] = (p.x + width / 2) / width;
|
||||||
|
data[index + 1] = (p.y + height / 2) / height;
|
||||||
|
data[index + 2] = p.value;
|
||||||
|
data[index + 3] = 0.0;
|
||||||
|
});
|
||||||
|
|
||||||
|
const texture = new THREE.DataTexture(data, MAX_POINTS, 1, THREE.RGBAFormat, THREE.FloatType);
|
||||||
|
texture.needsUpdate = true;
|
||||||
|
return texture;
|
||||||
|
}, [points, width, height]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
uniformsRef.current.u_radius.value = radius;
|
||||||
|
uniformsRef.current.u_opacity.value = opacity;
|
||||||
|
uniformsRef.current.u_debugMode.value = debugModeMap[debugMode];
|
||||||
|
}, [radius, opacity, debugMode, debugModeMap]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
uniformsRef.current.u_points.value = pointTexture;
|
||||||
|
uniformsRef.current.u_count.value = points.length;
|
||||||
|
}, [pointTexture, points.length]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<mesh
|
||||||
|
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={`
|
||||||
|
precision highp float;
|
||||||
|
uniform sampler2D u_points;
|
||||||
|
uniform int u_count;
|
||||||
|
uniform float u_radius;
|
||||||
|
uniform float u_opacity;
|
||||||
|
uniform int u_debugMode;
|
||||||
|
varying vec2 vUv;
|
||||||
|
|
||||||
|
const int MAX_POINTS = ${MAX_POINTS};
|
||||||
|
|
||||||
|
float gauss(float dist, float radius) {
|
||||||
|
return exp(-pow(dist / radius, 2.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Debug mode 0: solid red for plane visibility
|
||||||
|
if (u_debugMode == 0) {
|
||||||
|
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float intensity = 0.0;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_POINTS; i++) {
|
||||||
|
if (i >= u_count) break;
|
||||||
|
float fi = float(i) + 0.5;
|
||||||
|
float u = fi / float(MAX_POINTS);
|
||||||
|
|
||||||
|
vec4 point = texture2D(u_points, vec2(u, 0.5));
|
||||||
|
vec2 pos = point.rg;
|
||||||
|
float value = point.b;
|
||||||
|
|
||||||
|
float d = distance(vUv, pos);
|
||||||
|
intensity += value * gauss(d, u_radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug mode 1: grayscale
|
||||||
|
if (u_debugMode == 1) {
|
||||||
|
gl_FragColor = vec4(vec3(intensity), 1.0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug mode 2: heatmap color
|
||||||
|
vec3 color = vec3(0.0);
|
||||||
|
if (intensity > 0.0) {
|
||||||
|
if (intensity < 0.5) {
|
||||||
|
color = mix(vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 0.0), intensity * 2.0);
|
||||||
|
} else {
|
||||||
|
color = mix(vec3(0.0, 1.0, 0.0), vec3(1.0, 0.0, 0.0), (intensity - 0.5) * 2.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_FragColor = vec4(color, intensity * u_opacity);
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
</mesh>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HeatMap;
|
||||||
@@ -14,6 +14,7 @@ import Trigger from './triggers/trigger';
|
|||||||
import useModuleStore from '../../store/useModuleStore';
|
import useModuleStore from '../../store/useModuleStore';
|
||||||
import SimulationAnalysis from './analysis/simulationAnalysis';
|
import SimulationAnalysis from './analysis/simulationAnalysis';
|
||||||
import { useSceneContext } from '../scene/sceneContext';
|
import { useSceneContext } from '../scene/sceneContext';
|
||||||
|
import HeatMap from '../../components/HeatMap/HeatMap';
|
||||||
|
|
||||||
function Simulation() {
|
function Simulation() {
|
||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
@@ -62,6 +63,8 @@ function Simulation() {
|
|||||||
|
|
||||||
<SimulationAnalysis />
|
<SimulationAnalysis />
|
||||||
|
|
||||||
|
<HeatMap />
|
||||||
|
|
||||||
</>
|
</>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user