add useHeatMapStore for managing monitoring of vehicles and humans in HeatMap component
This commit is contained in:
@@ -6,6 +6,7 @@ import { useSceneContext } from "../../modules/scene/sceneContext";
|
|||||||
import * as CONSTANTS from "../../types/world/worldConstants";
|
import * as CONSTANTS from "../../types/world/worldConstants";
|
||||||
import { determineExecutionMachineSequences } from "../../modules/simulation/simulator/functions/determineExecutionMachineSequences";
|
import { determineExecutionMachineSequences } from "../../modules/simulation/simulator/functions/determineExecutionMachineSequences";
|
||||||
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from "../../store/usePlayButtonStore";
|
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from "../../store/usePlayButtonStore";
|
||||||
|
import { useHeatMapStore } from "../../store/simulation/useHeatMapStore";
|
||||||
|
|
||||||
const DECAY_RATE = 0.01;
|
const DECAY_RATE = 0.01;
|
||||||
const GROWTH_TIME_MULTIPLIER = 20;
|
const GROWTH_TIME_MULTIPLIER = 20;
|
||||||
@@ -32,6 +33,7 @@ const HeatMap = () => {
|
|||||||
const { getProductById, products } = productStore();
|
const { getProductById, products } = productStore();
|
||||||
const { selectedProductStore } = useProductContext();
|
const { selectedProductStore } = useProductContext();
|
||||||
const { selectedProduct } = selectedProductStore();
|
const { selectedProduct } = selectedProductStore();
|
||||||
|
const { hasHuman, hasVehicle, monitoringHuman, monitoringVehicle, addMonitoringHuman, addMonitoringVehicle } = useHeatMapStore();
|
||||||
const { isPlaying } = usePlayButtonStore();
|
const { isPlaying } = usePlayButtonStore();
|
||||||
const { isReset } = useResetButtonStore();
|
const { isReset } = useResetButtonStore();
|
||||||
const { isPaused } = usePauseButtonStore();
|
const { isPaused } = usePauseButtonStore();
|
||||||
@@ -62,6 +64,11 @@ const HeatMap = () => {
|
|||||||
}
|
}
|
||||||
}, [isReset, isPlaying]);
|
}, [isReset, isPlaying]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
addMonitoringVehicle("26770368-55e8-4d40-87f7-8eacb48dc236");
|
||||||
|
addMonitoringHuman("264a51e7-d8b9-4093-95ac-fa7e2dc49cfa");
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const selectedProductData = getProductById(selectedProduct.productUuid);
|
const selectedProductData = getProductById(selectedProduct.productUuid);
|
||||||
const newEvents: EventsSchema[] = [];
|
const newEvents: EventsSchema[] = [];
|
||||||
@@ -70,21 +77,26 @@ const HeatMap = () => {
|
|||||||
determineExecutionMachineSequences([selectedProductData]).then((sequences) => {
|
determineExecutionMachineSequences([selectedProductData]).then((sequences) => {
|
||||||
sequences.forEach((sequence) => {
|
sequences.forEach((sequence) => {
|
||||||
sequence.forEach((event) => {
|
sequence.forEach((event) => {
|
||||||
if (event.type === "human" || event.type === "vehicle") {
|
if (event.type === "human") {
|
||||||
newEvents.push(event);
|
if (hasHuman(event.modelUuid)) {
|
||||||
|
newEvents.push(event);
|
||||||
|
}
|
||||||
|
} else if (event.type === "vehicle") {
|
||||||
|
if (hasVehicle(event.modelUuid)) {
|
||||||
|
newEvents.push(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
setEvents(newEvents);
|
setEvents(newEvents);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [selectedProduct, products]);
|
}, [selectedProduct, products, monitoringHuman, monitoringVehicle]);
|
||||||
|
|
||||||
useFrame((state) => {
|
useFrame((state) => {
|
||||||
if (!scene || !isPlaying || isReset) return;
|
if (!scene || !isPlaying || isReset) return;
|
||||||
|
|
||||||
const now = state.clock.elapsedTime;
|
const now = state.clock.elapsedTime;
|
||||||
|
|
||||||
if (isPaused || speed === 0) {
|
if (isPaused || speed === 0) {
|
||||||
lastFrameTime.current = now;
|
lastFrameTime.current = now;
|
||||||
return;
|
return;
|
||||||
@@ -97,14 +109,11 @@ const HeatMap = () => {
|
|||||||
lastFrameTime.current = now;
|
lastFrameTime.current = now;
|
||||||
|
|
||||||
if (delta <= 0) return;
|
if (delta <= 0) return;
|
||||||
|
|
||||||
const updateInterval = UPDATE_INTERVAL / Math.max(speed, 0.1);
|
const updateInterval = UPDATE_INTERVAL / Math.max(speed, 0.1);
|
||||||
|
|
||||||
if (now - lastUpdateTime.current < updateInterval) return;
|
if (now - lastUpdateTime.current < updateInterval) return;
|
||||||
lastUpdateTime.current = now;
|
lastUpdateTime.current = now;
|
||||||
|
|
||||||
const scaledDelta = delta * speed;
|
const scaledDelta = delta * speed;
|
||||||
|
|
||||||
let updatedPoints = [...points];
|
let updatedPoints = [...points];
|
||||||
|
|
||||||
events.forEach((event) => {
|
events.forEach((event) => {
|
||||||
@@ -113,35 +122,23 @@ const HeatMap = () => {
|
|||||||
|
|
||||||
const pos = isUsingBBOX
|
const pos = isUsingBBOX
|
||||||
? (() => {
|
? (() => {
|
||||||
const box = new THREE.Box3().setFromObject(model);
|
const box = new THREE.Box3().setFromObject(model);
|
||||||
const { min, max } = box;
|
const { min, max } = box;
|
||||||
return new THREE.Vector3((min.x + max.x) / 2, 0, (min.z + max.z) / 2);
|
return new THREE.Vector3((min.x + max.x) / 2, 0, (min.z + max.z) / 2);
|
||||||
})()
|
})()
|
||||||
: model.position;
|
: model.position;
|
||||||
|
|
||||||
updatedPoints.push({
|
updatedPoints.push({ x: pos.x, y: pos.z, strength: 0.3, lastUpdated: now });
|
||||||
x: pos.x,
|
|
||||||
y: pos.z,
|
|
||||||
strength: 0.3,
|
|
||||||
lastUpdated: now,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
updatedPoints = updatedPoints
|
updatedPoints = updatedPoints.map((p) => ({ ...p, strength: Math.max(0, p.strength - DECAY_RATE * scaledDelta) })).filter((p) => p.strength > 0.01);
|
||||||
.map((p) => ({
|
|
||||||
...p,
|
|
||||||
strength: Math.max(0, p.strength - DECAY_RATE * scaledDelta),
|
|
||||||
}))
|
|
||||||
.filter((p) => p.strength > 0.01);
|
|
||||||
|
|
||||||
setPoints(updatedPoints);
|
setPoints(updatedPoints);
|
||||||
});
|
});
|
||||||
|
|
||||||
const pointTexture = useMemo(() => {
|
const pointTexture = useMemo(() => {
|
||||||
if (points.length === 0) return null;
|
if (points.length === 0) return null;
|
||||||
|
|
||||||
const data = new Float32Array(points.length * 4);
|
const data = new Float32Array(points.length * 4);
|
||||||
|
|
||||||
points.forEach((p, i) => {
|
points.forEach((p, i) => {
|
||||||
const index = i * 4;
|
const index = i * 4;
|
||||||
data[index] = (p.x + width / 2) / width;
|
data[index] = (p.x + width / 2) / width;
|
||||||
@@ -150,13 +147,7 @@ const HeatMap = () => {
|
|||||||
data[index + 3] = 0.0;
|
data[index + 3] = 0.0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const texture = new THREE.DataTexture(
|
const texture = new THREE.DataTexture(data, points.length, 1, THREE.RGBAFormat, THREE.FloatType);
|
||||||
data,
|
|
||||||
points.length,
|
|
||||||
1,
|
|
||||||
THREE.RGBAFormat,
|
|
||||||
THREE.FloatType
|
|
||||||
);
|
|
||||||
texture.needsUpdate = true;
|
texture.needsUpdate = true;
|
||||||
return texture;
|
return texture;
|
||||||
}, [points, width, height]);
|
}, [points, width, height]);
|
||||||
@@ -234,7 +225,7 @@ const HeatMap = () => {
|
|||||||
float adjustedIntensity = intensity * u_growthRate;
|
float adjustedIntensity = intensity * u_growthRate;
|
||||||
|
|
||||||
// Normalize intensity between 0-1
|
// Normalize intensity between 0-1
|
||||||
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);
|
vec3 color = vec3(0.0);
|
||||||
if (normalized < 0.33) {
|
if (normalized < 0.33) {
|
||||||
|
|||||||
100
app/src/store/simulation/useHeatMapStore.ts
Normal file
100
app/src/store/simulation/useHeatMapStore.ts
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import { create } from "zustand";
|
||||||
|
import { immer } from "zustand/middleware/immer";
|
||||||
|
|
||||||
|
interface HeatMapState {
|
||||||
|
monitoringVehicle: string[];
|
||||||
|
monitoringHuman: string[];
|
||||||
|
|
||||||
|
setMonitoringVehicle: (vehiclesUuid: string[]) => void;
|
||||||
|
addMonitoringVehicle: (vehicleUuid: string) => void;
|
||||||
|
removeMonitoringVehicle: (vehicleUuid: string) => void;
|
||||||
|
clearMonitoringVehicle: () => void;
|
||||||
|
|
||||||
|
setMonitoringHuman: (humansUuid: string[]) => void;
|
||||||
|
addMonitoringHuman: (humanUuid: string) => void;
|
||||||
|
removeMonitoringHuman: (humanUuid: string) => void;
|
||||||
|
clearMonitoringHuman: () => void;
|
||||||
|
|
||||||
|
clearAllMonitors: () => void;
|
||||||
|
|
||||||
|
hasVehicle: (vehicleUuid: string) => boolean;
|
||||||
|
hasHuman: (humanUuid: string) => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useHeatMapStore = create<HeatMapState>()(
|
||||||
|
immer((set, get) => ({
|
||||||
|
monitoringVehicle: [],
|
||||||
|
monitoringHuman: [],
|
||||||
|
|
||||||
|
setMonitoringVehicle: (vehiclesUuid) =>
|
||||||
|
set((state) => {
|
||||||
|
state.monitoringVehicle = vehiclesUuid;
|
||||||
|
}),
|
||||||
|
|
||||||
|
addMonitoringVehicle: (vehicleUuid) =>
|
||||||
|
set((state) => {
|
||||||
|
if (!state.monitoringVehicle.includes(vehicleUuid)) {
|
||||||
|
state.monitoringVehicle.push(vehicleUuid);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
removeMonitoringVehicle: (vehicleUuid) =>
|
||||||
|
set((state) => {
|
||||||
|
state.monitoringVehicle = state.monitoringVehicle.filter((v) => v !== vehicleUuid);
|
||||||
|
}),
|
||||||
|
|
||||||
|
clearMonitoringVehicle: () =>
|
||||||
|
set((state) => {
|
||||||
|
state.monitoringVehicle = [];
|
||||||
|
}),
|
||||||
|
|
||||||
|
setMonitoringHuman: (humansUuid) =>
|
||||||
|
set((state) => {
|
||||||
|
state.monitoringHuman = humansUuid;
|
||||||
|
}),
|
||||||
|
|
||||||
|
addMonitoringHuman: (humanUuid) =>
|
||||||
|
set((state) => {
|
||||||
|
if (!state.monitoringHuman.includes(humanUuid)) {
|
||||||
|
state.monitoringHuman.push(humanUuid);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
removeMonitoringHuman: (humanUuid) =>
|
||||||
|
set((state) => {
|
||||||
|
state.monitoringHuman = state.monitoringHuman.filter((h) => h !== humanUuid);
|
||||||
|
}),
|
||||||
|
|
||||||
|
clearMonitoringHuman: () =>
|
||||||
|
set((state) => {
|
||||||
|
state.monitoringHuman = [];
|
||||||
|
}),
|
||||||
|
|
||||||
|
clearAllMonitors: () =>
|
||||||
|
set((state) => {
|
||||||
|
state.monitoringVehicle = [];
|
||||||
|
state.monitoringHuman = [];
|
||||||
|
}),
|
||||||
|
|
||||||
|
hasVehicle: (vehicleUuid) => get().monitoringVehicle.includes(vehicleUuid),
|
||||||
|
hasHuman: (humanUuid) => get().monitoringHuman.includes(humanUuid),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// const selectedProductData = getProductById(selectedProduct.productUuid);
|
||||||
|
// const newEvents: EventsSchema[] = [];
|
||||||
|
|
||||||
|
// if (selectedProductData) {
|
||||||
|
// determineExecutionMachineSequences([selectedProductData]).then((sequences) => {
|
||||||
|
// sequences.forEach((sequence) => {
|
||||||
|
// sequence.forEach((event) => {
|
||||||
|
// if (event.type === "human" || event.type === "vehicle") {
|
||||||
|
// newEvents.push(event);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// setEvents(newEvents);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }, [selectedProduct, products, monitoringHuman, monitoringVehicle]);
|
||||||
Reference in New Issue
Block a user