Merge branch 'v2' into simulation-armbot-v2
This commit is contained in:
@@ -38,18 +38,12 @@ const SimulationPlayer: React.FC = () => {
|
|||||||
const { isReset, setReset } = useResetButtonStore();
|
const { isReset, setReset } = useResetButtonStore();
|
||||||
const { subModule } = useSubModuleStore();
|
const { subModule } = useSubModuleStore();
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isReset) {
|
|
||||||
setTimeout(()=>{
|
|
||||||
setReset(false);
|
|
||||||
},0)
|
|
||||||
}
|
|
||||||
}, [isReset])
|
|
||||||
|
|
||||||
// Button functions
|
// Button functions
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
setReset(true);
|
setReset(true);
|
||||||
|
setIsPaused(false);
|
||||||
setSpeed(1);
|
setSpeed(1);
|
||||||
|
setPlaySimulation(false); // local state reset
|
||||||
};
|
};
|
||||||
const handlePlayStop = () => {
|
const handlePlayStop = () => {
|
||||||
setIsPaused(!isPaused);
|
setIsPaused(!isPaused);
|
||||||
@@ -58,6 +52,7 @@ const SimulationPlayer: React.FC = () => {
|
|||||||
const handleExit = () => {
|
const handleExit = () => {
|
||||||
setPlaySimulation(false);
|
setPlaySimulation(false);
|
||||||
setIsPlaying(false);
|
setIsPlaying(false);
|
||||||
|
setIsPaused(false);
|
||||||
setActiveTool("cursor");
|
setActiveTool("cursor");
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -279,10 +274,11 @@ const SimulationPlayer: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
{index < intervals.length - 1 && (
|
{index < intervals.length - 1 && (
|
||||||
<div
|
<div
|
||||||
className={`line ${progress >= ((index + 1) / totalSegments) * 100
|
className={`line ${
|
||||||
|
progress >= ((index + 1) / totalSegments) * 100
|
||||||
? "filled"
|
? "filled"
|
||||||
: ""
|
: ""
|
||||||
}`}
|
}`}
|
||||||
></div>
|
></div>
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
@@ -4,28 +4,44 @@ import { useFrame } from "@react-three/fiber";
|
|||||||
import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore";
|
import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore";
|
||||||
import { useProductStore } from "../../../../../store/simulation/useProductStore";
|
import { useProductStore } from "../../../../../store/simulation/useProductStore";
|
||||||
import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore";
|
import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore";
|
||||||
|
import { usePlayButtonStore, useAnimationPlaySpeed, usePauseButtonStore, useResetButtonStore } from "../../../../../store/usePlayButtonStore";
|
||||||
|
|
||||||
export function useSpawnHandler() {
|
interface SpawnInstance {
|
||||||
const { addMaterial } = useMaterialStore();
|
lastSpawnTime: number | null;
|
||||||
const { getModelUuidByActionUuid, getPointUuidByActionUuid } = useProductStore();
|
startTime: number;
|
||||||
const { selectedProduct } = useSelectedProduct();
|
spawnCount: number;
|
||||||
const lastSpawnTime = useRef<number | null>(null);
|
params: {
|
||||||
const startTime = useRef<number | null>(null);
|
|
||||||
const spawnCountRef = useRef<number>(0);
|
|
||||||
const spawnParams = useRef<{
|
|
||||||
material: string;
|
material: string;
|
||||||
intervalMs: number;
|
intervalMs: number;
|
||||||
totalCount: number;
|
totalCount: number;
|
||||||
action: ConveyorAction;
|
action: ConveyorAction;
|
||||||
} | null>(null);
|
};
|
||||||
|
pauseStartTime: number;
|
||||||
|
remainingTime: number;
|
||||||
|
isPaused: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const clearCurrentSpawn = useCallback(() => {
|
export function useSpawnHandler() {
|
||||||
lastSpawnTime.current = null;
|
const { addMaterial } = useMaterialStore();
|
||||||
startTime.current = null;
|
const { getModelUuidByActionUuid, getPointUuidByActionUuid } = useProductStore();
|
||||||
spawnCountRef.current = 0;
|
const { isPlaying } = usePlayButtonStore();
|
||||||
spawnParams.current = null;
|
const { isPaused } = usePauseButtonStore();
|
||||||
|
const { speed } = useAnimationPlaySpeed();
|
||||||
|
const { isReset } = useResetButtonStore();
|
||||||
|
const { selectedProduct } = useSelectedProduct();
|
||||||
|
|
||||||
|
const activeSpawns = useRef<Map<string, SpawnInstance>>(new Map());
|
||||||
|
|
||||||
|
const clearAllSpawns = useCallback(() => {
|
||||||
|
activeSpawns.current.clear();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isReset) {
|
||||||
|
clearAllSpawns();
|
||||||
|
}
|
||||||
|
}, [isReset, clearAllSpawns]);
|
||||||
|
|
||||||
const spawnLogStatus = (materialUuid: string, status: string) => {
|
const spawnLogStatus = (materialUuid: string, status: string) => {
|
||||||
// console.log(`${materialUuid}, ${status}`);
|
// console.log(`${materialUuid}, ${status}`);
|
||||||
}
|
}
|
||||||
@@ -64,78 +80,121 @@ export function useSpawnHandler() {
|
|||||||
|
|
||||||
addMaterial(newMaterial);
|
addMaterial(newMaterial);
|
||||||
return newMaterial;
|
return newMaterial;
|
||||||
}, [addMaterial]);
|
}, [addMaterial, getModelUuidByActionUuid, getPointUuidByActionUuid, selectedProduct.productId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const currentTime = performance.now();
|
||||||
|
|
||||||
|
activeSpawns.current.forEach((spawn) => {
|
||||||
|
if (isPaused && !spawn.isPaused) {
|
||||||
|
if (spawn.lastSpawnTime === null) {
|
||||||
|
spawn.remainingTime = Math.max(0, spawn.params.intervalMs - (currentTime - spawn.startTime));
|
||||||
|
} else {
|
||||||
|
spawn.remainingTime = Math.max(0, spawn.params.intervalMs - (currentTime - spawn.lastSpawnTime));
|
||||||
|
}
|
||||||
|
spawn.pauseStartTime = currentTime;
|
||||||
|
spawn.isPaused = true;
|
||||||
|
} else if (!isPaused && spawn.isPaused) {
|
||||||
|
if (spawn.remainingTime > 0) {
|
||||||
|
if (spawn.lastSpawnTime === null) {
|
||||||
|
spawn.startTime = currentTime - (spawn.params.intervalMs - spawn.remainingTime);
|
||||||
|
} else {
|
||||||
|
spawn.lastSpawnTime = currentTime - (spawn.params.intervalMs - spawn.remainingTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spawn.isPaused = false;
|
||||||
|
spawn.pauseStartTime = 0;
|
||||||
|
spawn.remainingTime = 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [isPaused]);
|
||||||
|
|
||||||
useFrame(() => {
|
useFrame(() => {
|
||||||
if (!spawnParams.current || !startTime.current) return;
|
if (!isPlaying || isPaused || isReset) return;
|
||||||
|
|
||||||
const currentTime = performance.now();
|
const currentTime = performance.now();
|
||||||
const { material, intervalMs, totalCount, action } = spawnParams.current;
|
const completedActions: string[] = [];
|
||||||
const isFirstSpawn = lastSpawnTime.current === null;
|
|
||||||
const elapsed = currentTime - startTime.current;
|
|
||||||
|
|
||||||
// First spawn
|
activeSpawns.current.forEach((spawn, actionUuid) => {
|
||||||
if (isFirstSpawn) {
|
const { material, intervalMs, totalCount, action } = spawn.params;
|
||||||
if (elapsed >= intervalMs) {
|
const isFirstSpawn = spawn.lastSpawnTime === null;
|
||||||
const createdMaterial = createNewMaterial(material, action);
|
|
||||||
if (createdMaterial) {
|
// First spawn
|
||||||
spawnLogStatus(createdMaterial.materialId, `[${elapsed.toFixed(2)}ms] Spawned ${material} (1/${totalCount})`);
|
if (isFirstSpawn) {
|
||||||
|
const elapsed = currentTime - spawn.startTime;
|
||||||
|
if (elapsed >= intervalMs) {
|
||||||
|
const createdMaterial = createNewMaterial(material, action);
|
||||||
|
if (createdMaterial) {
|
||||||
|
spawnLogStatus(createdMaterial.materialId, `[${elapsed.toFixed(2)}ms] Spawned ${material} (1/${totalCount})`);
|
||||||
|
}
|
||||||
|
spawn.lastSpawnTime = currentTime;
|
||||||
|
spawn.spawnCount = 1;
|
||||||
|
|
||||||
|
if (totalCount <= 1) {
|
||||||
|
completedActions.push(actionUuid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
lastSpawnTime.current = currentTime;
|
return;
|
||||||
spawnCountRef.current = 1;
|
}
|
||||||
|
|
||||||
if (totalCount <= 1) {
|
// Subsequent spawns
|
||||||
clearCurrentSpawn();
|
if (spawn.lastSpawnTime !== null) {
|
||||||
|
const timeSinceLast = currentTime - spawn.lastSpawnTime;
|
||||||
|
if (timeSinceLast >= intervalMs) {
|
||||||
|
const count = spawn.spawnCount + 1;
|
||||||
|
const createdMaterial = createNewMaterial(material, action);
|
||||||
|
if (createdMaterial) {
|
||||||
|
spawnLogStatus(createdMaterial.materialId, `[${timeSinceLast.toFixed(2)}ms] Spawned ${material} (${count}/${totalCount})`);
|
||||||
|
}
|
||||||
|
spawn.lastSpawnTime = currentTime;
|
||||||
|
spawn.spawnCount = count;
|
||||||
|
|
||||||
|
if (count >= totalCount) {
|
||||||
|
completedActions.push(actionUuid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// Subsequent spawns
|
completedActions.forEach(actionUuid => {
|
||||||
if (lastSpawnTime.current !== null) {
|
activeSpawns.current.delete(actionUuid);
|
||||||
const timeSinceLast = currentTime - lastSpawnTime.current;
|
});
|
||||||
if (timeSinceLast >= intervalMs) {
|
|
||||||
const count = spawnCountRef.current + 1;
|
|
||||||
const createdMaterial = createNewMaterial(material, action);
|
|
||||||
if (createdMaterial) {
|
|
||||||
spawnLogStatus(createdMaterial.materialId, `[${elapsed.toFixed(2)}ms] Spawned ${material} (${count}/${totalCount})`);
|
|
||||||
}
|
|
||||||
lastSpawnTime.current = currentTime;
|
|
||||||
spawnCountRef.current = count;
|
|
||||||
|
|
||||||
if (count >= totalCount) {
|
|
||||||
clearCurrentSpawn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSpawn = useCallback((action: ConveyorAction) => {
|
const handleSpawn = useCallback((action: ConveyorAction) => {
|
||||||
if (!action || action.actionType !== 'spawn') return;
|
if (!action || action.actionType !== 'spawn') return;
|
||||||
|
|
||||||
const { material, spawnInterval = 0, spawnCount = 1 } = action;
|
const { material, spawnInterval = 0, spawnCount = 1, actionUuid } = action;
|
||||||
const intervalMs = spawnInterval * 1000;
|
const intervalMs = spawnInterval * 1000;
|
||||||
|
|
||||||
clearCurrentSpawn();
|
if (activeSpawns.current.has(actionUuid)) {
|
||||||
|
activeSpawns.current.delete(actionUuid);
|
||||||
|
}
|
||||||
|
|
||||||
spawnParams.current = {
|
activeSpawns.current.set(actionUuid, {
|
||||||
material,
|
lastSpawnTime: null,
|
||||||
intervalMs,
|
startTime: performance.now(),
|
||||||
totalCount: spawnCount,
|
spawnCount: 0,
|
||||||
action: action
|
params: {
|
||||||
};
|
material,
|
||||||
|
intervalMs,
|
||||||
startTime.current = performance.now();
|
totalCount: spawnCount,
|
||||||
}, [clearCurrentSpawn]);
|
action: action
|
||||||
|
},
|
||||||
|
pauseStartTime: 0,
|
||||||
|
remainingTime: 0,
|
||||||
|
isPaused: false
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
clearCurrentSpawn();
|
clearAllSpawns();
|
||||||
};
|
};
|
||||||
}, [clearCurrentSpawn]);
|
}, [clearAllSpawns]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handleSpawn,
|
handleSpawn,
|
||||||
clearCurrentSpawn
|
clearCurrentSpawn: clearAllSpawns
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,8 @@ export function useConveyorActions() {
|
|||||||
const { handleSpawn, clearCurrentSpawn } = useSpawnHandler();
|
const { handleSpawn, clearCurrentSpawn } = useSpawnHandler();
|
||||||
|
|
||||||
const handleDefaultAction = useCallback((action: ConveyorAction) => {
|
const handleDefaultAction = useCallback((action: ConveyorAction) => {
|
||||||
// console.log(`Default conveyor action ${action.actionUuid}`);
|
console.log('action: ', action);
|
||||||
|
console.log(`Default conveyor action ${action.actionUuid}`);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleSpawnAction = useCallback((action: ConveyorAction) => {
|
const handleSpawnAction = useCallback((action: ConveyorAction) => {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { usePlayButtonStore, useResetButtonStore } from "../../../store/usePlayButtonStore";
|
||||||
import { useConveyorActions } from "./conveyor/useConveyorActions";
|
import { useConveyorActions } from "./conveyor/useConveyorActions";
|
||||||
import { useMachineActions } from "./machine/useMachineActions";
|
import { useMachineActions } from "./machine/useMachineActions";
|
||||||
import { useRoboticArmActions } from "./roboticArm/useRoboticArmActions";
|
import { useRoboticArmActions } from "./roboticArm/useRoboticArmActions";
|
||||||
@@ -6,6 +7,8 @@ import { useVehicleActions } from "./vehicle/useVehicleActions";
|
|||||||
import { useCallback, useEffect } from "react";
|
import { useCallback, useEffect } from "react";
|
||||||
|
|
||||||
export function useActionHandler() {
|
export function useActionHandler() {
|
||||||
|
const { isReset } = useResetButtonStore();
|
||||||
|
const { isPlaying } = usePlayButtonStore();
|
||||||
const { handleConveyorAction, cleanup: cleanupConveyor } = useConveyorActions();
|
const { handleConveyorAction, cleanup: cleanupConveyor } = useConveyorActions();
|
||||||
const { handleVehicleAction, cleanup: cleanupVehicle } = useVehicleActions();
|
const { handleVehicleAction, cleanup: cleanupVehicle } = useVehicleActions();
|
||||||
const { handleRoboticArmAction, cleanup: cleanupRoboticArm } = useRoboticArmActions();
|
const { handleRoboticArmAction, cleanup: cleanupRoboticArm } = useRoboticArmActions();
|
||||||
@@ -52,7 +55,7 @@ export function useActionHandler() {
|
|||||||
return () => {
|
return () => {
|
||||||
cleanup();
|
cleanup();
|
||||||
};
|
};
|
||||||
}, [cleanup]);
|
}, [cleanup, isReset, isPlaying]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handleAction,
|
handleAction,
|
||||||
|
|||||||
@@ -1,276 +1,265 @@
|
|||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useEventsStore } from "../../../../../store/simulation/useEventsStore";
|
import { useEventsStore } from "../../../../../store/simulation/useEventsStore";
|
||||||
import useModuleStore, {
|
import useModuleStore, { useSubModuleStore } from "../../../../../store/useModuleStore";
|
||||||
useSubModuleStore,
|
|
||||||
} from "../../../../../store/useModuleStore";
|
|
||||||
import { TransformControls } from "@react-three/drei";
|
import { TransformControls } from "@react-three/drei";
|
||||||
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
|
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
|
||||||
import {
|
import { useSelectedEventSphere, useSelectedEventData, } from "../../../../../store/simulation/useSimulationStore";
|
||||||
useSelectedEventSphere,
|
|
||||||
useSelectedEventData,
|
|
||||||
} from "../../../../../store/simulation/useSimulationStore";
|
|
||||||
import { useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
|
import { usePlayButtonStore } from "../../../../../store/usePlayButtonStore";
|
||||||
|
|
||||||
function PointsCreator() {
|
function PointsCreator() {
|
||||||
const { gl, raycaster, scene, pointer, camera } = useThree();
|
const { gl, raycaster, scene, pointer, camera } = useThree();
|
||||||
const { subModule } = useSubModuleStore();
|
const { subModule } = useSubModuleStore();
|
||||||
const { events, updatePoint, getPointByUuid, getEventByModelUuid } =
|
const { events, updatePoint, getPointByUuid, getEventByModelUuid } = useEventsStore();
|
||||||
useEventsStore();
|
const { activeModule } = useModuleStore();
|
||||||
const { activeModule } = useModuleStore();
|
const transformRef = useRef<any>(null);
|
||||||
const transformRef = useRef<any>(null);
|
const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null);
|
||||||
const [transformMode, setTransformMode] = useState<
|
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
|
||||||
"translate" | "rotate" | null
|
const { selectedEventSphere, setSelectedEventSphere, clearSelectedEventSphere, } = useSelectedEventSphere();
|
||||||
>(null);
|
const { setSelectedEventData, clearSelectedEventData } = useSelectedEventData();
|
||||||
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
|
const { isPlaying } = usePlayButtonStore();
|
||||||
const {
|
|
||||||
selectedEventSphere,
|
|
||||||
setSelectedEventSphere,
|
|
||||||
clearSelectedEventSphere,
|
|
||||||
} = useSelectedEventSphere();
|
|
||||||
const { setSelectedEventData, clearSelectedEventData } =
|
|
||||||
useSelectedEventData();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedEventSphere) {
|
if (selectedEventSphere) {
|
||||||
const eventData = getEventByModelUuid(
|
const eventData = getEventByModelUuid(
|
||||||
selectedEventSphere.userData.modelUuid
|
selectedEventSphere.userData.modelUuid
|
||||||
);
|
);
|
||||||
|
|
||||||
if (eventData) {
|
if (eventData) {
|
||||||
setSelectedEventData(eventData, selectedEventSphere.userData.pointUuid);
|
setSelectedEventData(eventData, selectedEventSphere.userData.pointUuid);
|
||||||
} else {
|
} else {
|
||||||
clearSelectedEventData();
|
clearSelectedEventData();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
clearSelectedEventData();
|
clearSelectedEventData();
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [selectedEventSphere]);
|
}, [selectedEventSphere]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
const keyCombination = detectModifierKeys(e);
|
const keyCombination = detectModifierKeys(e);
|
||||||
if (!selectedEventSphere) return;
|
if (!selectedEventSphere) return;
|
||||||
if (keyCombination === "G") {
|
if (keyCombination === "G") {
|
||||||
setTransformMode((prev) => (prev === "translate" ? null : "translate"));
|
setTransformMode((prev) => (prev === "translate" ? null : "translate"));
|
||||||
}
|
}
|
||||||
if (keyCombination === "R") {
|
if (keyCombination === "R") {
|
||||||
setTransformMode((prev) => (prev === "rotate" ? null : "rotate"));
|
setTransformMode((prev) => (prev === "rotate" ? null : "rotate"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("keydown", handleKeyDown);
|
window.addEventListener("keydown", handleKeyDown);
|
||||||
return () => window.removeEventListener("keydown", handleKeyDown);
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||||
}, [selectedEventSphere]);
|
}, [selectedEventSphere]);
|
||||||
|
|
||||||
const updatePointToState = (selectedEventSphere: THREE.Mesh) => {
|
const updatePointToState = (selectedEventSphere: THREE.Mesh) => {
|
||||||
let point = JSON.parse(
|
let point = JSON.parse(
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
getPointByUuid(
|
getPointByUuid(
|
||||||
selectedEventSphere.userData.modelUuid,
|
selectedEventSphere.userData.modelUuid,
|
||||||
selectedEventSphere.userData.pointUuid
|
selectedEventSphere.userData.pointUuid
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
if (point) {
|
if (point) {
|
||||||
point.position = [
|
point.position = [
|
||||||
selectedEventSphere.position.x,
|
selectedEventSphere.position.x,
|
||||||
selectedEventSphere.position.y,
|
selectedEventSphere.position.y,
|
||||||
selectedEventSphere.position.z,
|
selectedEventSphere.position.z,
|
||||||
];
|
];
|
||||||
updatePoint(
|
updatePoint(
|
||||||
selectedEventSphere.userData.modelUuid,
|
selectedEventSphere.userData.modelUuid,
|
||||||
selectedEventSphere.userData.pointUuid,
|
selectedEventSphere.userData.pointUuid,
|
||||||
point
|
point
|
||||||
);
|
);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const canvasElement = gl.domElement;
|
|
||||||
|
|
||||||
let drag = false;
|
|
||||||
let isMouseDown = false;
|
|
||||||
|
|
||||||
const onMouseDown = () => {
|
|
||||||
isMouseDown = true;
|
|
||||||
drag = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onMouseUp = () => {
|
|
||||||
if (selectedEventSphere && !drag) {
|
|
||||||
raycaster.setFromCamera(pointer, camera);
|
|
||||||
const intersects = raycaster
|
|
||||||
.intersectObjects(scene.children, true)
|
|
||||||
.filter((intersect) => intersect.object.name === "Event-Sphere");
|
|
||||||
if (intersects.length === 0) {
|
|
||||||
clearSelectedEventSphere();
|
|
||||||
setTransformMode(null);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onMouseMove = () => {
|
useEffect(() => {
|
||||||
if (isMouseDown) {
|
const canvasElement = gl.domElement;
|
||||||
drag = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (subModule === "mechanics") {
|
let drag = false;
|
||||||
canvasElement.addEventListener("mousedown", onMouseDown);
|
let isMouseDown = false;
|
||||||
canvasElement.addEventListener("mouseup", onMouseUp);
|
|
||||||
canvasElement.addEventListener("mousemove", onMouseMove);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
const onMouseDown = () => {
|
||||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
isMouseDown = true;
|
||||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
drag = false;
|
||||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
};
|
||||||
};
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [gl, subModule, selectedEventSphere]);
|
|
||||||
|
|
||||||
return (
|
const onMouseUp = () => {
|
||||||
<>
|
if (selectedEventSphere && !drag) {
|
||||||
{activeModule === "simulation" && (
|
raycaster.setFromCamera(pointer, camera);
|
||||||
|
const intersects = raycaster
|
||||||
|
.intersectObjects(scene.children, true)
|
||||||
|
.filter((intersect) => intersect.object.name === "Event-Sphere");
|
||||||
|
if (intersects.length === 0) {
|
||||||
|
clearSelectedEventSphere();
|
||||||
|
setTransformMode(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseMove = () => {
|
||||||
|
if (isMouseDown) {
|
||||||
|
drag = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (subModule === "mechanics") {
|
||||||
|
canvasElement.addEventListener("mousedown", onMouseDown);
|
||||||
|
canvasElement.addEventListener("mouseup", onMouseUp);
|
||||||
|
canvasElement.addEventListener("mousemove", onMouseMove);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||||
|
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||||
|
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [gl, subModule, selectedEventSphere]);
|
||||||
|
|
||||||
|
return (
|
||||||
<>
|
<>
|
||||||
<group name="EventPointsGroup">
|
{activeModule === "simulation" && (
|
||||||
{events.map((event, index) => {
|
<>
|
||||||
if (event.type === "transfer") {
|
<group name="EventPointsGroup" visible={!isPlaying}>
|
||||||
return (
|
{events.map((event, index) => {
|
||||||
<group
|
if (event.type === "transfer") {
|
||||||
key={`${index}-${event.modelUuid}`}
|
return (
|
||||||
position={event.position}
|
<group
|
||||||
rotation={event.rotation}
|
key={`${index}-${event.modelUuid}`}
|
||||||
>
|
position={event.position}
|
||||||
{event.points.map((point, j) => (
|
rotation={event.rotation}
|
||||||
<mesh
|
>
|
||||||
name="Event-Sphere"
|
{event.points.map((point, j) => (
|
||||||
uuid={point.uuid}
|
<mesh
|
||||||
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
|
name="Event-Sphere"
|
||||||
onClick={(e) => {
|
uuid={point.uuid}
|
||||||
e.stopPropagation();
|
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
|
||||||
setSelectedEventSphere(
|
onClick={(e) => {
|
||||||
sphereRefs.current[point.uuid]
|
e.stopPropagation();
|
||||||
);
|
setSelectedEventSphere(
|
||||||
}}
|
sphereRefs.current[point.uuid]
|
||||||
key={`${index}-${point.uuid}`}
|
);
|
||||||
position={new THREE.Vector3(...point.position)}
|
}}
|
||||||
userData={{
|
key={`${index}-${point.uuid}`}
|
||||||
modelUuid: event.modelUuid,
|
position={new THREE.Vector3(...point.position)}
|
||||||
pointUuid: point.uuid,
|
userData={{
|
||||||
}}
|
modelUuid: event.modelUuid,
|
||||||
>
|
pointUuid: point.uuid,
|
||||||
<sphereGeometry args={[0.1, 16, 16]} />
|
}}
|
||||||
<meshStandardMaterial color="orange" />
|
>
|
||||||
</mesh>
|
<sphereGeometry args={[0.1, 16, 16]} />
|
||||||
))}
|
<meshStandardMaterial color="orange" />
|
||||||
</group>
|
</mesh>
|
||||||
);
|
))}
|
||||||
} else if (event.type === "vehicle") {
|
</group>
|
||||||
return (
|
);
|
||||||
<group
|
} else if (event.type === "vehicle") {
|
||||||
key={`${index}-${event.modelUuid}`}
|
return (
|
||||||
position={event.position}
|
<group
|
||||||
rotation={event.rotation}
|
key={`${index}-${event.modelUuid}`}
|
||||||
>
|
position={event.position}
|
||||||
<mesh
|
rotation={event.rotation}
|
||||||
name="Event-Sphere"
|
>
|
||||||
uuid={event.point.uuid}
|
<mesh
|
||||||
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
|
name="Event-Sphere"
|
||||||
onClick={(e) => {
|
uuid={event.point.uuid}
|
||||||
e.stopPropagation();
|
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
|
||||||
setSelectedEventSphere(
|
onClick={(e) => {
|
||||||
sphereRefs.current[event.point.uuid]
|
e.stopPropagation();
|
||||||
);
|
setSelectedEventSphere(
|
||||||
}}
|
sphereRefs.current[event.point.uuid]
|
||||||
position={new THREE.Vector3(...event.point.position)}
|
);
|
||||||
userData={{
|
}}
|
||||||
modelUuid: event.modelUuid,
|
position={new THREE.Vector3(...event.point.position)}
|
||||||
pointUuid: event.point.uuid,
|
userData={{
|
||||||
}}
|
modelUuid: event.modelUuid,
|
||||||
>
|
pointUuid: event.point.uuid,
|
||||||
<sphereGeometry args={[0.1, 16, 16]} />
|
}}
|
||||||
<meshStandardMaterial color="blue" />
|
>
|
||||||
</mesh>
|
<sphereGeometry args={[0.1, 16, 16]} />
|
||||||
</group>
|
<meshStandardMaterial color="blue" />
|
||||||
);
|
</mesh>
|
||||||
} else if (event.type === "roboticArm") {
|
</group>
|
||||||
return (
|
);
|
||||||
<group
|
} else if (event.type === "roboticArm") {
|
||||||
key={`${index}-${event.modelUuid}`}
|
return (
|
||||||
position={event.position}
|
<group
|
||||||
rotation={event.rotation}
|
key={`${index}-${event.modelUuid}`}
|
||||||
>
|
position={event.position}
|
||||||
<mesh
|
rotation={event.rotation}
|
||||||
name="Event-Sphere"
|
>
|
||||||
uuid={event.point.uuid}
|
<mesh
|
||||||
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
|
name="Event-Sphere"
|
||||||
onClick={(e) => {
|
uuid={event.point.uuid}
|
||||||
e.stopPropagation();
|
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
|
||||||
setSelectedEventSphere(
|
onClick={(e) => {
|
||||||
sphereRefs.current[event.point.uuid]
|
e.stopPropagation();
|
||||||
);
|
setSelectedEventSphere(
|
||||||
}}
|
sphereRefs.current[event.point.uuid]
|
||||||
position={new THREE.Vector3(...event.point.position)}
|
);
|
||||||
userData={{
|
}}
|
||||||
modelUuid: event.modelUuid,
|
position={new THREE.Vector3(...event.point.position)}
|
||||||
pointUuid: event.point.uuid,
|
userData={{
|
||||||
}}
|
modelUuid: event.modelUuid,
|
||||||
>
|
pointUuid: event.point.uuid,
|
||||||
<sphereGeometry args={[0.1, 16, 16]} />
|
}}
|
||||||
<meshStandardMaterial color="green" />
|
>
|
||||||
</mesh>
|
<sphereGeometry args={[0.1, 16, 16]} />
|
||||||
</group>
|
<meshStandardMaterial color="green" />
|
||||||
);
|
</mesh>
|
||||||
} else if (event.type === "machine") {
|
</group>
|
||||||
return (
|
);
|
||||||
<group
|
} else if (event.type === "machine") {
|
||||||
key={`${index}-${event.modelUuid}`}
|
return (
|
||||||
position={event.position}
|
<group
|
||||||
rotation={event.rotation}
|
key={`${index}-${event.modelUuid}`}
|
||||||
>
|
position={event.position}
|
||||||
<mesh
|
rotation={event.rotation}
|
||||||
name="Event-Sphere"
|
>
|
||||||
uuid={event.point.uuid}
|
<mesh
|
||||||
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
|
name="Event-Sphere"
|
||||||
onClick={(e) => {
|
uuid={event.point.uuid}
|
||||||
e.stopPropagation();
|
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
|
||||||
setSelectedEventSphere(
|
onClick={(e) => {
|
||||||
sphereRefs.current[event.point.uuid]
|
e.stopPropagation();
|
||||||
);
|
setSelectedEventSphere(
|
||||||
}}
|
sphereRefs.current[event.point.uuid]
|
||||||
position={new THREE.Vector3(...event.point.position)}
|
);
|
||||||
userData={{
|
}}
|
||||||
modelUuid: event.modelUuid,
|
position={new THREE.Vector3(...event.point.position)}
|
||||||
pointUuid: event.point.uuid,
|
userData={{
|
||||||
}}
|
modelUuid: event.modelUuid,
|
||||||
>
|
pointUuid: event.point.uuid,
|
||||||
<sphereGeometry args={[0.1, 16, 16]} />
|
}}
|
||||||
<meshStandardMaterial color="purple" />
|
>
|
||||||
</mesh>
|
<sphereGeometry args={[0.1, 16, 16]} />
|
||||||
</group>
|
<meshStandardMaterial color="purple" />
|
||||||
);
|
</mesh>
|
||||||
} else {
|
</group>
|
||||||
return null;
|
);
|
||||||
}
|
} else {
|
||||||
})}
|
return null;
|
||||||
</group>
|
}
|
||||||
{selectedEventSphere && transformMode && (
|
})}
|
||||||
<TransformControls
|
</group>
|
||||||
ref={transformRef}
|
{selectedEventSphere && transformMode && (
|
||||||
object={selectedEventSphere}
|
<TransformControls
|
||||||
mode={transformMode}
|
ref={transformRef}
|
||||||
onMouseUp={(e) => {
|
object={selectedEventSphere}
|
||||||
updatePointToState(selectedEventSphere);
|
mode={transformMode}
|
||||||
}}
|
onMouseUp={(e) => {
|
||||||
/>
|
updatePointToState(selectedEventSphere);
|
||||||
)}
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
);
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PointsCreator;
|
export default PointsCreator;
|
||||||
|
|||||||
@@ -34,16 +34,12 @@ const MachineAnimator = ({ currentPhase, handleCallBack, processingTime, machine
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
isPlayingRef.current = isPlaying;
|
isPlayingRef.current = isPlaying;
|
||||||
}, [isPlaying]);
|
}, [isPlaying]);
|
||||||
useEffect(() => {
|
|
||||||
isResetRef.current = isReset;
|
|
||||||
}, [isReset]);
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
if (isReset || !isPlaying) {
|
if (isReset || !isPlaying) {
|
||||||
reset();
|
reset();
|
||||||
setReset(false);
|
|
||||||
startTimeRef.current = 0;
|
startTimeRef.current = 0;
|
||||||
isPausedRef.current = false;
|
isPausedRef.current = false;
|
||||||
pauseTimeRef.current = 0;
|
pauseTimeRef.current = 0;
|
||||||
@@ -53,49 +49,53 @@ const MachineAnimator = ({ currentPhase, handleCallBack, processingTime, machine
|
|||||||
}
|
}
|
||||||
}, [isReset, isPlaying])
|
}, [isReset, isPlaying])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentPhase === 'processing' && !animationStarted.current && machineUuid) {
|
if (currentPhase === 'processing' && !animationStarted.current && machineUuid) {
|
||||||
animationStarted.current = true;
|
animationStarted.current = true;
|
||||||
startTimeRef.current = performance.now();
|
startTimeRef.current = performance.now();
|
||||||
animationFrameId.current = requestAnimationFrame(step);
|
animationFrameId.current = requestAnimationFrame(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
}, [currentPhase]);
|
}, [currentPhase]);
|
||||||
|
|
||||||
function step(time: number) {
|
function step(time: number) {
|
||||||
if (!isPausedRef.current || !isResetRef.current) {
|
|
||||||
if (animationFrameId.current) {
|
if (isPausedRef.current) {
|
||||||
|
if (!pauseTimeRef.current) {
|
||||||
|
pauseTimeRef.current = performance.now();
|
||||||
|
}
|
||||||
|
animationFrameId.current = requestAnimationFrame(step);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pauseTimeRef.current) {
|
||||||
|
const pauseDuration = performance.now() - pauseTimeRef.current;
|
||||||
|
startTimeRef.current += pauseDuration;
|
||||||
|
pauseTimeRef.current = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const elapsed = time - startTimeRef.current;
|
||||||
|
const processedTime = (processingTime * 1000) / speed;
|
||||||
|
|
||||||
|
if (elapsed < processedTime) {
|
||||||
|
machineStatus(machineUuid, "Machine is currently processing the task");
|
||||||
|
animationFrameId.current = requestAnimationFrame(step);
|
||||||
|
} else {
|
||||||
|
animationStarted.current = false;
|
||||||
|
if (animationFrameId.current !== null) {
|
||||||
|
removeCurrentAction(machineUuid);
|
||||||
cancelAnimationFrame(animationFrameId.current);
|
cancelAnimationFrame(animationFrameId.current);
|
||||||
animationFrameId.current = null;
|
animationFrameId.current = null;
|
||||||
}
|
|
||||||
if (isPausedRef.current) {
|
|
||||||
if (!pauseTimeRef.current) {
|
|
||||||
pauseTimeRef.current = performance.now();
|
|
||||||
}
|
|
||||||
animationFrameId.current = requestAnimationFrame(step);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pauseTimeRef.current) {
|
|
||||||
const pauseDuration = performance.now() - pauseTimeRef.current;
|
|
||||||
startTimeRef.current += pauseDuration;
|
|
||||||
pauseTimeRef.current = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const elapsed = time - startTimeRef.current;
|
|
||||||
const processedTime = processingTime * 1000;
|
|
||||||
if (elapsed < processedTime) {
|
|
||||||
machineStatus(machineUuid, "Machine is currently processing the task");
|
|
||||||
animationFrameId.current = requestAnimationFrame(step);
|
|
||||||
} else {
|
|
||||||
removeCurrentAction(machineUuid);
|
|
||||||
animationStarted.current = false;
|
|
||||||
handleCallBack();
|
handleCallBack();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ function MachineInstance({ machineDetail }: any) {
|
|||||||
const { machines, addCurrentAction, setMachineState, setMachineActive } = useMachineStore();
|
const { machines, addCurrentAction, setMachineState, setMachineActive } = useMachineStore();
|
||||||
|
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
|
setCurrentPhase("idle");
|
||||||
setMachineState(machineDetail.modelUuid, 'idle');
|
setMachineState(machineDetail.modelUuid, 'idle');
|
||||||
setMachineActive(machineDetail.modelUuid, false);
|
setMachineActive(machineDetail.modelUuid, false);
|
||||||
isIncrememtable.current = true;
|
isIncrememtable.current = true;
|
||||||
setCurrentPhase("idle");
|
|
||||||
}
|
}
|
||||||
const increment = () => {
|
const increment = () => {
|
||||||
if (isIncrememtable.current) {
|
if (isIncrememtable.current) {
|
||||||
@@ -31,7 +31,7 @@ function MachineInstance({ machineDetail }: any) {
|
|||||||
if (!machineDetail.isActive && machineDetail.state === "idle" && currentPhase == "idle" && !machineDetail.currentAction) {
|
if (!machineDetail.isActive && machineDetail.state === "idle" && currentPhase == "idle" && !machineDetail.currentAction) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
increment();
|
increment();
|
||||||
}, 2000);
|
}, 5000);
|
||||||
machineStatus(machineDetail.modelUuid, 'Machine is idle and waiting for next instruction.')
|
machineStatus(machineDetail.modelUuid, 'Machine is idle and waiting for next instruction.')
|
||||||
} else if (!machineDetail.isActive && machineDetail.state === "idle" && currentPhase == "idle" && machineDetail.currentAction) {
|
} else if (!machineDetail.isActive && machineDetail.state === "idle" && currentPhase == "idle" && machineDetail.currentAction) {
|
||||||
setCurrentPhase("processing");
|
setCurrentPhase("processing");
|
||||||
@@ -39,8 +39,6 @@ function MachineInstance({ machineDetail }: any) {
|
|||||||
setMachineActive(machineDetail.modelUuid, true);
|
setMachineActive(machineDetail.modelUuid, true);
|
||||||
machineStatus(machineDetail.modelUuid, "Machine started processing")
|
machineStatus(machineDetail.modelUuid, "Machine started processing")
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
reset();
|
|
||||||
}
|
}
|
||||||
}, [currentPhase, isPlaying, machines])
|
}, [currentPhase, isPlaying, machines])
|
||||||
|
|
||||||
@@ -60,6 +58,7 @@ function MachineInstance({ machineDetail }: any) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MachineAnimator processingTime={machineDetail.point.action.processTime} handleCallBack={handleCallBack} currentPhase={currentPhase} machineUuid={machineDetail.modelUuid} machineStatus={machineStatus} reset={reset} />
|
<MachineAnimator processingTime={machineDetail.point.action.processTime} handleCallBack={handleCallBack} currentPhase={currentPhase} machineUuid={machineDetail.modelUuid} machineStatus={machineStatus} reset={reset} />
|
||||||
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,35 @@
|
|||||||
import React, { useEffect, useState, useRef } from 'react';
|
import React, { useEffect, useState, useRef } from 'react';
|
||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { useFrame, useThree } from '@react-three/fiber';
|
import { useFrame, useThree } from '@react-three/fiber';
|
||||||
|
import { usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||||
|
|
||||||
interface MaterialAnimatorProps {
|
interface MaterialAnimatorProps {
|
||||||
matRef: React.RefObject<THREE.Mesh>;
|
matRef: React.RefObject<THREE.Mesh>;
|
||||||
material: MaterialSchema;
|
material: MaterialSchema;
|
||||||
speed: number; // units per second
|
currentSpeed: number;
|
||||||
onAnimationComplete?: () => void;
|
onAnimationComplete?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function MaterialAnimator({
|
function MaterialAnimator({
|
||||||
matRef,
|
matRef,
|
||||||
material,
|
material,
|
||||||
speed,
|
currentSpeed,
|
||||||
onAnimationComplete
|
onAnimationComplete
|
||||||
}: MaterialAnimatorProps) {
|
}: MaterialAnimatorProps) {
|
||||||
const { scene } = useThree();
|
const { scene } = useThree();
|
||||||
const [targetPosition, setTargetPosition] = useState<THREE.Vector3 | null>(null);
|
const [targetPosition, setTargetPosition] = useState<THREE.Vector3 | null>(null);
|
||||||
const [isAnimating, setIsAnimating] = useState(false);
|
const [isAnimating, setIsAnimating] = useState(false);
|
||||||
const animationStartTime = useRef<number>(0);
|
const animationState = useRef({
|
||||||
const startPosition = useRef<THREE.Vector3>(new THREE.Vector3());
|
startTime: 0,
|
||||||
const totalDistance = useRef<number>(0);
|
startPosition: new THREE.Vector3(),
|
||||||
|
totalDistance: 0,
|
||||||
|
pausedTime: 0,
|
||||||
|
isPaused: false,
|
||||||
|
lastFrameTime: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
const { isPlaying } = usePlayButtonStore();
|
||||||
|
const { isPaused } = usePauseButtonStore();
|
||||||
|
|
||||||
const getWorldPosition = (uuid: string): THREE.Vector3 | null => {
|
const getWorldPosition = (uuid: string): THREE.Vector3 | null => {
|
||||||
const obj = scene.getObjectByProperty('uuid', uuid);
|
const obj = scene.getObjectByProperty('uuid', uuid);
|
||||||
@@ -30,37 +39,58 @@ function MaterialAnimator({
|
|||||||
return position;
|
return position;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle target position changes and play state
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!material.next?.pointUuid) {
|
if (!isPlaying || !material.next?.pointUuid) {
|
||||||
setTargetPosition(null);
|
|
||||||
setIsAnimating(false);
|
setIsAnimating(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newTarget = getWorldPosition(material.next.pointUuid);
|
const newTarget = getWorldPosition(material.next.pointUuid);
|
||||||
if (newTarget && matRef.current) {
|
if (newTarget && matRef.current) {
|
||||||
startPosition.current.copy(matRef.current.position);
|
animationState.current.startPosition.copy(matRef.current.position);
|
||||||
totalDistance.current = startPosition.current.distanceTo(newTarget);
|
animationState.current.totalDistance = animationState.current.startPosition.distanceTo(newTarget);
|
||||||
animationStartTime.current = performance.now();
|
animationState.current.startTime = performance.now() - animationState.current.pausedTime;
|
||||||
|
animationState.current.pausedTime = 0;
|
||||||
|
animationState.current.isPaused = false;
|
||||||
setTargetPosition(newTarget);
|
setTargetPosition(newTarget);
|
||||||
setIsAnimating(true);
|
setIsAnimating(true);
|
||||||
}
|
}
|
||||||
}, [material.next?.pointUuid]);
|
}, [material.next?.pointUuid, isPlaying]);
|
||||||
|
|
||||||
|
// Handle pause/unpause
|
||||||
|
useEffect(() => {
|
||||||
|
if (isPaused) {
|
||||||
|
animationState.current.isPaused = true;
|
||||||
|
setIsAnimating(false);
|
||||||
|
// Record the time when paused
|
||||||
|
animationState.current.pausedTime = performance.now() - animationState.current.startTime;
|
||||||
|
} else {
|
||||||
|
animationState.current.isPaused = false;
|
||||||
|
if (isPlaying && targetPosition && !isAnimating) {
|
||||||
|
// Resume from where we left off
|
||||||
|
animationState.current.startTime = performance.now() - animationState.current.pausedTime;
|
||||||
|
setIsAnimating(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [isPaused]);
|
||||||
|
|
||||||
useFrame(() => {
|
useFrame(() => {
|
||||||
if (!matRef.current || !targetPosition || !isAnimating) return;
|
if (!matRef.current || !targetPosition || !isAnimating || animationState.current.isPaused || !isPlaying) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentTime = performance.now();
|
||||||
|
// Calculate elapsed time since animation start, minus any paused time
|
||||||
|
const elapsed = (currentTime - animationState.current.startTime) / 1000;
|
||||||
|
const progress = Math.min(1, (currentSpeed * elapsed) / animationState.current.totalDistance);
|
||||||
|
|
||||||
// Calculate exact position based on constant speed
|
|
||||||
const elapsed = (performance.now() - animationStartTime.current) / 1000;
|
|
||||||
const progress = Math.min(1, (speed * elapsed) / totalDistance.current);
|
|
||||||
|
|
||||||
matRef.current.position.lerpVectors(
|
matRef.current.position.lerpVectors(
|
||||||
startPosition.current,
|
animationState.current.startPosition,
|
||||||
targetPosition,
|
targetPosition,
|
||||||
progress
|
progress
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check if animation is complete
|
|
||||||
if (progress >= 1) {
|
if (progress >= 1) {
|
||||||
matRef.current.position.copy(targetPosition);
|
matRef.current.position.copy(targetPosition);
|
||||||
setIsAnimating(false);
|
setIsAnimating(false);
|
||||||
|
|||||||
@@ -5,12 +5,16 @@ import { useProductStore } from '../../../../../store/simulation/useProductStore
|
|||||||
import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore';
|
import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore';
|
||||||
import { MaterialModel } from '../material/materialModel';
|
import { MaterialModel } from '../material/materialModel';
|
||||||
import { useThree } from '@react-three/fiber';
|
import { useThree } from '@react-three/fiber';
|
||||||
|
import { useAnimationPlaySpeed } from '../../../../../store/usePlayButtonStore';
|
||||||
|
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
|
||||||
|
|
||||||
function MaterialInstance({ material }: { material: MaterialSchema }) {
|
function MaterialInstance({ material }: { material: MaterialSchema }) {
|
||||||
const matRef: any = useRef();
|
const matRef: any = useRef();
|
||||||
const { scene } = useThree();
|
const { scene } = useThree();
|
||||||
const { getModelUuidByPointUuid, getPointByUuid, getEventByModelUuid } = useProductStore();
|
const { getModelUuidByPointUuid, getPointByUuid, getEventByModelUuid, getActionByUuid } = useProductStore();
|
||||||
const { selectedProduct } = useSelectedProduct();
|
const { selectedProduct } = useSelectedProduct();
|
||||||
|
const { speed } = useAnimationPlaySpeed();
|
||||||
|
const { triggerPointActions } = useTriggerHandler();
|
||||||
|
|
||||||
const getWorldPositionFromScene = (pointUuid: string): THREE.Vector3 | null => {
|
const getWorldPositionFromScene = (pointUuid: string): THREE.Vector3 | null => {
|
||||||
const pointObj = scene.getObjectByProperty("uuid", pointUuid);
|
const pointObj = scene.getObjectByProperty("uuid", pointUuid);
|
||||||
@@ -21,32 +25,32 @@ function MaterialInstance({ material }: { material: MaterialSchema }) {
|
|||||||
return worldPosition;
|
return worldPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
const { position, rotation, speed } = useMemo(() => {
|
const { position, rotation, currentSpeed } = useMemo(() => {
|
||||||
if (!material.current?.pointUuid) {
|
if (!material.current?.pointUuid) {
|
||||||
return { position: new THREE.Vector3(0, 0, 0), rotation: new THREE.Vector3(0, 0, 0), speed: 1 };
|
return { position: new THREE.Vector3(0, 0, 0), rotation: new THREE.Vector3(0, 0, 0), currentSpeed: 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
const modelUuid = getModelUuidByPointUuid(selectedProduct.productId, material.current.pointUuid);
|
const modelUuid = getModelUuidByPointUuid(selectedProduct.productId, material.current.pointUuid);
|
||||||
if (!modelUuid) {
|
if (!modelUuid) {
|
||||||
return { position: new THREE.Vector3(0, 0, 0), rotation: new THREE.Vector3(0, 0, 0), speed: 1 };
|
return { position: new THREE.Vector3(0, 0, 0), rotation: new THREE.Vector3(0, 0, 0), currentSpeed: 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
const speed = getCurrentSpeed(selectedProduct.productId, modelUuid);
|
const currentSpeed = getCurrentSpeed(selectedProduct.productId, modelUuid);
|
||||||
|
|
||||||
const point = getPointByUuid(selectedProduct.productId, modelUuid, material.current.pointUuid);
|
const point = getPointByUuid(selectedProduct.productId, modelUuid, material.current.pointUuid);
|
||||||
if (!point) {
|
if (!point) {
|
||||||
return { position: new THREE.Vector3(0, 0, 0), rotation: new THREE.Vector3(0, 0, 0), speed: 1 };
|
return { position: new THREE.Vector3(0, 0, 0), rotation: new THREE.Vector3(0, 0, 0), currentSpeed: 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
const position = getWorldPositionFromScene(point.uuid);
|
const position = getWorldPositionFromScene(point.uuid);
|
||||||
if (position) {
|
if (position) {
|
||||||
return { position: position, rotation: new THREE.Vector3(0, 0, 0), speed: 1 };
|
return { position: position, rotation: new THREE.Vector3(0, 0, 0), currentSpeed: 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
position: new THREE.Vector3(...point.position),
|
position: new THREE.Vector3(...point.position),
|
||||||
rotation: new THREE.Vector3(...point.rotation),
|
rotation: new THREE.Vector3(...point.rotation),
|
||||||
speed: speed || 1
|
currentSpeed: currentSpeed || 1
|
||||||
};
|
};
|
||||||
}, [material, getPointByUuid]);
|
}, [material, getPointByUuid]);
|
||||||
|
|
||||||
@@ -82,16 +86,25 @@ function MaterialInstance({ material }: { material: MaterialSchema }) {
|
|||||||
// console.log('material: ', material);
|
// console.log('material: ', material);
|
||||||
}, [material])
|
}, [material])
|
||||||
|
|
||||||
|
const callTrigger = () => {
|
||||||
|
const action = getActionByUuid(selectedProduct.productId, material.current.actionUuid)
|
||||||
|
if (action) {
|
||||||
|
triggerPointActions(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
||||||
<MaterialModel matRef={matRef} materialType={material.materialType} position={position} />
|
{material.isRendered &&
|
||||||
|
<MaterialModel matRef={matRef} materialType={material.materialType} visible={material.isVisible} position={position} />
|
||||||
|
}
|
||||||
|
|
||||||
<MaterialAnimator
|
<MaterialAnimator
|
||||||
matRef={matRef}
|
matRef={matRef}
|
||||||
material={material}
|
material={material}
|
||||||
speed={speed}
|
currentSpeed={currentSpeed * speed}
|
||||||
onAnimationComplete={() => { console.log('123');}}
|
onAnimationComplete={() => { callTrigger() }}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ function MaterialInstances() {
|
|||||||
const { materials } = useMaterialStore();
|
const { materials } = useMaterialStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// console.log('materials: ', materials);
|
console.log('materials: ', materials);
|
||||||
}, [materials])
|
}, [materials])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,11 +1,27 @@
|
|||||||
import React from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import MaterialInstances from './instances/materialInstances'
|
import MaterialInstances from './instances/materialInstances'
|
||||||
|
import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayButtonStore';
|
||||||
|
import { useMaterialStore } from '../../../store/simulation/useMaterialStore';
|
||||||
|
|
||||||
function Materials() {
|
function Materials() {
|
||||||
|
const { clearMaterials } = useMaterialStore();
|
||||||
|
const { isPlaying } = usePlayButtonStore();
|
||||||
|
const { isReset } = useResetButtonStore();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isReset || !isPlaying) {
|
||||||
|
clearMaterials();
|
||||||
|
}
|
||||||
|
}, [isReset, isPlaying]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
||||||
<MaterialInstances />
|
{isPlaying &&
|
||||||
|
|
||||||
|
<MaterialInstances />
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useProductStore } from '../../../store/simulation/useProductStore';
|
import { useProductStore } from '../../../store/simulation/useProductStore';
|
||||||
import { useActionHandler } from '../actions/useActionHandler';
|
import { useActionHandler } from '../actions/useActionHandler';
|
||||||
|
import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayButtonStore';
|
||||||
|
|
||||||
function Simulator() {
|
function Simulator() {
|
||||||
const { products } = useProductStore();
|
const { products } = useProductStore();
|
||||||
const { handleAction } = useActionHandler();
|
const { handleAction } = useActionHandler();
|
||||||
|
const { isPlaying } = usePlayButtonStore();
|
||||||
|
const { isReset } = useResetButtonStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!isPlaying || isReset) return;
|
||||||
|
|
||||||
const executionOrder = determineExecutionOrder(products);
|
const executionOrder = determineExecutionOrder(products);
|
||||||
executionOrder.forEach(point => {
|
executionOrder.map(point => {
|
||||||
if ('actions' in point) {
|
const action = 'actions' in point ? point.actions[0] : point.action;
|
||||||
handleAction(point.actions[0]);
|
handleAction(action);
|
||||||
} else {
|
|
||||||
handleAction(point.action);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}, [products, handleAction]);
|
}, [products, handleAction, isPlaying, isReset]);
|
||||||
|
|
||||||
function determineExecutionOrder(products: productsSchema): PointsScheme[] {
|
function determineExecutionOrder(products: productsSchema): PointsScheme[] {
|
||||||
// Create maps for all events and points
|
// Create maps for all events and points
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { handleAddEventToProduct } from "../../events/points/functions/handleAdd
|
|||||||
import { QuadraticBezierLine } from "@react-three/drei";
|
import { QuadraticBezierLine } from "@react-three/drei";
|
||||||
import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi";
|
import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi";
|
||||||
import { useDeleteTool } from "../../../../store/store";
|
import { useDeleteTool } from "../../../../store/store";
|
||||||
|
import { usePlayButtonStore } from "../../../../store/usePlayButtonStore";
|
||||||
|
|
||||||
interface ConnectionLine {
|
interface ConnectionLine {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -29,6 +30,7 @@ function TriggerConnector() {
|
|||||||
const [helperlineColor, setHelperLineColor] = useState<string>("red");
|
const [helperlineColor, setHelperLineColor] = useState<string>("red");
|
||||||
const [currentLine, setCurrentLine] = useState<{ start: THREE.Vector3; end: THREE.Vector3; mid: THREE.Vector3; } | null>(null);
|
const [currentLine, setCurrentLine] = useState<{ start: THREE.Vector3; end: THREE.Vector3; mid: THREE.Vector3; } | null>(null);
|
||||||
const { deleteTool } = useDeleteTool();
|
const { deleteTool } = useDeleteTool();
|
||||||
|
const { isPlaying } = usePlayButtonStore();
|
||||||
|
|
||||||
const [firstSelectedPoint, setFirstSelectedPoint] = useState<{
|
const [firstSelectedPoint, setFirstSelectedPoint] = useState<{
|
||||||
productId: string;
|
productId: string;
|
||||||
@@ -424,7 +426,7 @@ function TriggerConnector() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group name="simulationConnectionGroup" >
|
<group name="simulationConnectionGroup" visible={!isPlaying}>
|
||||||
{connections.map((connection) => {
|
{connections.map((connection) => {
|
||||||
const startPoint = getWorldPositionFromScene(connection.startPointUuid);
|
const startPoint = getWorldPositionFromScene(connection.startPointUuid);
|
||||||
const endPoint = getWorldPositionFromScene(connection.endPointUuid);
|
const endPoint = getWorldPositionFromScene(connection.endPointUuid);
|
||||||
|
|||||||
@@ -1,13 +1,128 @@
|
|||||||
import { useCallback, useEffect, useRef } from 'react';
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
import { useActionHandler } from '../../actions/useActionHandler';
|
import { useActionHandler } from '../../actions/useActionHandler';
|
||||||
import { useProductStore } from '../../../../store/simulation/useProductStore';
|
import { useProductStore } from '../../../../store/simulation/useProductStore';
|
||||||
|
import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore';
|
||||||
|
import { useMaterialStore } from '../../../../store/simulation/useMaterialStore';
|
||||||
|
|
||||||
export function useTriggerHandler() {
|
export function useTriggerHandler() {
|
||||||
const { getActionByUuid } = useProductStore();
|
const { getActionByUuid, getEventByTriggerUuid, getEventByModelUuid } = useProductStore();
|
||||||
const { handleAction } = useActionHandler();
|
const { handleAction } = useActionHandler();
|
||||||
|
const { getMaterialByCurrentModelUuid, setCurrentLocation, setNextLocation } = useMaterialStore();
|
||||||
|
const { selectedProduct } = useSelectedProduct();
|
||||||
|
|
||||||
const handleTrigger = (trigger: TriggerSchema) => {
|
const handleTrigger = (trigger: TriggerSchema, actionUuid: string) => {
|
||||||
|
|
||||||
|
// const fromEvent = getEventByTriggerUuid(selectedProduct.productId, trigger.triggerUuid);
|
||||||
|
// console.log('fromEvent: ', fromEvent);
|
||||||
|
|
||||||
|
// const toEvent = getEventByModelUuid(selectedProduct.productId, trigger.triggeredAsset?.triggeredModel.modelUuid || '');
|
||||||
|
// console.log('toEvent: ', toEvent);
|
||||||
|
|
||||||
|
// if (fromEvent?.type === 'transfer') {
|
||||||
|
// if (toEvent?.type === 'transfer') {
|
||||||
|
// // console.log('toEvent: ', toEvent.type);
|
||||||
|
// // Transfer to Transfer
|
||||||
|
// const action = getActionByUuid(selectedProduct.productId, trigger.triggeredAsset?.triggeredAction?.actionUuid || '');
|
||||||
|
// if (action && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
|
||||||
|
// const material = getMaterialByCurrentModelUuid(fromEvent.modelUuid);
|
||||||
|
// if (material) {
|
||||||
|
// if (material.next &&
|
||||||
|
// action.triggers[0].triggeredAsset?.triggeredAction?.actionUuid &&
|
||||||
|
// action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid) {
|
||||||
|
|
||||||
|
// setCurrentLocation(material.materialId, material.next);
|
||||||
|
|
||||||
|
// setNextLocation(material.materialId, {
|
||||||
|
// modelUuid: toEvent.modelUuid,
|
||||||
|
// pointUuid: action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid,
|
||||||
|
// actionUuid: action.triggers[0].triggeredAsset?.triggeredAction?.actionUuid
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// handleAction(action);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else if (toEvent?.type === 'vehicle') {
|
||||||
|
// // Transfer to Vehicle
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'machine') {
|
||||||
|
// // Transfer to Machine
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'roboticArm') {
|
||||||
|
// // Transfer to Robotic Arm
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'storageUnit') {
|
||||||
|
// // Transfer to Storage Unit
|
||||||
|
|
||||||
|
// }
|
||||||
|
// } else if (fromEvent?.type === 'vehicle') {
|
||||||
|
// if (toEvent?.type === 'transfer') {
|
||||||
|
// // Vehicle to Transfer
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'vehicle') {
|
||||||
|
// // Vehicle to Vehicle
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'machine') {
|
||||||
|
// // Vehicle to Machine
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'roboticArm') {
|
||||||
|
// // Vehicle to Robotic Arm
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'storageUnit') {
|
||||||
|
// // Vehicle to Storage Unit
|
||||||
|
|
||||||
|
// }
|
||||||
|
// } else if (fromEvent?.type === 'machine') {
|
||||||
|
// if (toEvent?.type === 'transfer') {
|
||||||
|
// // Machine to Transfer
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'vehicle') {
|
||||||
|
// // Machine to Vehicle
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'machine') {
|
||||||
|
// // Machine to Machine
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'roboticArm') {
|
||||||
|
// // Machine to Robotic Arm
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'storageUnit') {
|
||||||
|
// // Machine to Storage Unit
|
||||||
|
|
||||||
|
// }
|
||||||
|
// } else if (fromEvent?.type === 'roboticArm') {
|
||||||
|
// if (toEvent?.type === 'transfer') {
|
||||||
|
// // Robotic Arm to Transfer
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'vehicle') {
|
||||||
|
// // Robotic Arm to Vehicle
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'machine') {
|
||||||
|
// // Robotic Arm to Machine
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'roboticArm') {
|
||||||
|
// // Robotic Arm to Robotic Arm
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'storageUnit') {
|
||||||
|
// // Robotic Arm to Storage Unit
|
||||||
|
|
||||||
|
// }
|
||||||
|
// } else if (fromEvent?.type === 'storageUnit') {
|
||||||
|
// if (toEvent?.type === 'transfer') {
|
||||||
|
// // Storage Unit to Transfer
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'vehicle') {
|
||||||
|
// // Storage Unit to Vehicle
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'machine') {
|
||||||
|
// // Storage Unit to Machine
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'roboticArm') {
|
||||||
|
// // Storage Unit to Robotic Arm
|
||||||
|
|
||||||
|
// } else if (toEvent?.type === 'storageUnit') {
|
||||||
|
// // Storage Unit to Storage Unit
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
const triggerPointActions = useCallback((action: Action) => {
|
const triggerPointActions = useCallback((action: Action) => {
|
||||||
@@ -18,7 +133,7 @@ export function useTriggerHandler() {
|
|||||||
case 'onStart':
|
case 'onStart':
|
||||||
break;
|
break;
|
||||||
case 'onComplete':
|
case 'onComplete':
|
||||||
handleTrigger(trigger);
|
handleTrigger(trigger, action.actionUuid);
|
||||||
break;
|
break;
|
||||||
case 'onStop':
|
case 'onStop':
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { useThree, useFrame } from '@react-three/fiber';
|
||||||
|
import * as THREE from 'three';
|
||||||
|
import { MaterialModel } from '../../../materials/instances/material/materialModel';
|
||||||
|
import { Html } from '@react-three/drei';
|
||||||
|
|
||||||
|
type MaterialAnimatorProps = {
|
||||||
|
agvDetail: VehicleStatus;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const MaterialAnimator = ({ agvDetail }: MaterialAnimatorProps) => {
|
||||||
|
const meshRef = useRef<any>(null!);
|
||||||
|
const [hasLoad, setHasLoad] = useState(false);
|
||||||
|
const { scene } = useThree();
|
||||||
|
const offset = new THREE.Vector3(0, 0.85, 0);
|
||||||
|
const [htmlPosition, setHtmlPosition] = useState<[number, number, number]>([0, 0, 0]);
|
||||||
|
const [htmlRotation, setHtmlRotation] = useState<[number, number, number]>([0, 0, 0]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setHasLoad(agvDetail.currentLoad > 0);
|
||||||
|
}, [agvDetail.currentLoad]);
|
||||||
|
|
||||||
|
useFrame(() => {
|
||||||
|
if (!hasLoad || !meshRef.current) return;
|
||||||
|
|
||||||
|
const agvModel = scene.getObjectByProperty("uuid", agvDetail.modelUuid) as THREE.Object3D;
|
||||||
|
if (agvModel) {
|
||||||
|
const worldPosition = offset.clone().applyMatrix4(agvModel.matrixWorld);
|
||||||
|
meshRef.current.position.copy(worldPosition);
|
||||||
|
setHtmlPosition([worldPosition.x, worldPosition.y, worldPosition.z]);
|
||||||
|
meshRef.current.rotation.copy(agvModel.rotation);
|
||||||
|
setHtmlRotation([agvModel.rotation.x, agvModel.rotation.y, agvModel.rotation.z]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{hasLoad && (
|
||||||
|
<>
|
||||||
|
<MaterialModel
|
||||||
|
matRef={meshRef}
|
||||||
|
materialType={agvDetail.materialType || 'Default material'}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Html
|
||||||
|
position={htmlPosition}
|
||||||
|
rotation={htmlRotation}
|
||||||
|
style={{ backgroundColor: "pink", padding: "4px", borderRadius: "4px" }}
|
||||||
|
>
|
||||||
|
{agvDetail.currentLoad}
|
||||||
|
</Html>
|
||||||
|
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export default MaterialAnimator;
|
||||||
@@ -199,7 +199,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{currentPath.length > 0 && (
|
{currentPath.length > 0 && (
|
||||||
<>
|
<group visible={false}>
|
||||||
<Line points={currentPath} color="blue" lineWidth={3} />
|
<Line points={currentPath} color="blue" lineWidth={3} />
|
||||||
{currentPath.map((point, index) => (
|
{currentPath.map((point, index) => (
|
||||||
<mesh key={index} position={point}>
|
<mesh key={index} position={point}>
|
||||||
@@ -207,7 +207,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
|
|||||||
<meshStandardMaterial color="red" />
|
<meshStandardMaterial color="red" />
|
||||||
</mesh>
|
</mesh>
|
||||||
))}
|
))}
|
||||||
</>
|
</group>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,11 +5,13 @@ import { NavMeshQuery } from '@recast-navigation/core';
|
|||||||
import { useNavMesh } from '../../../../../store/store';
|
import { useNavMesh } from '../../../../../store/store';
|
||||||
import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
|
import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||||
import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore';
|
import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore';
|
||||||
|
import MaterialAnimator from '../animator/materialAnimator';
|
||||||
|
|
||||||
function VehicleInstance({ agvDetail }: any) {
|
function VehicleInstance({ agvDetail }: any) {
|
||||||
const { navMesh } = useNavMesh();
|
const { navMesh } = useNavMesh();
|
||||||
|
const vehicleRef: any = useRef();
|
||||||
const { isPlaying } = usePlayButtonStore();
|
const { isPlaying } = usePlayButtonStore();
|
||||||
const { vehicles, setVehicleActive, setVehicleState, incrementVehicleLoad } = useVehicleStore();
|
const { vehicles, setVehicleActive, setVehicleState, incrementVehicleLoad, setMaterialType } = useVehicleStore();
|
||||||
const [currentPhase, setCurrentPhase] = useState<string>('stationed');
|
const [currentPhase, setCurrentPhase] = useState<string>('stationed');
|
||||||
const [path, setPath] = useState<[number, number, number][]>([]);
|
const [path, setPath] = useState<[number, number, number][]>([]);
|
||||||
let isIncrememtable = useRef<boolean>(true);
|
let isIncrememtable = useRef<boolean>(true);
|
||||||
@@ -44,8 +46,8 @@ function VehicleInstance({ agvDetail }: any) {
|
|||||||
|
|
||||||
const increment = () => {
|
const increment = () => {
|
||||||
if (isIncrememtable.current) {
|
if (isIncrememtable.current) {
|
||||||
|
|
||||||
incrementVehicleLoad(agvDetail.modelUuid, 10);
|
incrementVehicleLoad(agvDetail.modelUuid, 10);
|
||||||
|
setMaterialType(agvDetail.modelUuid, 'Material 1')
|
||||||
isIncrememtable.current = false;
|
isIncrememtable.current = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,7 +73,7 @@ function VehicleInstance({ agvDetail }: any) {
|
|||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
|
|
||||||
if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity) {
|
if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity && agvDetail.materialType) {
|
||||||
const toDrop = computePath(
|
const toDrop = computePath(
|
||||||
agvDetail.point.action.pickUpPoint.position,
|
agvDetail.point.action.pickUpPoint.position,
|
||||||
agvDetail.point.action.unLoadPoint.position
|
agvDetail.point.action.unLoadPoint.position
|
||||||
@@ -118,6 +120,7 @@ function VehicleInstance({ agvDetail }: any) {
|
|||||||
setVehicleState(agvDetail.modelUuid, 'idle');
|
setVehicleState(agvDetail.modelUuid, 'idle');
|
||||||
setVehicleActive(agvDetail.modelUuid, false);
|
setVehicleActive(agvDetail.modelUuid, false);
|
||||||
setPath([]);
|
setPath([]);
|
||||||
|
setMaterialType(agvDetail.modelUuid, null)
|
||||||
vehicleStatus(agvDetail.modelUuid, 'Reached pickup point again, cycle complete');
|
vehicleStatus(agvDetail.modelUuid, 'Reached pickup point again, cycle complete');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,6 +135,7 @@ function VehicleInstance({ agvDetail }: any) {
|
|||||||
agvDetail={agvDetail}
|
agvDetail={agvDetail}
|
||||||
reset={reset}
|
reset={reset}
|
||||||
/>
|
/>
|
||||||
|
<MaterialAnimator agvDetail={agvDetail} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ type MaterialsStore = {
|
|||||||
|
|
||||||
addMaterial: (material: MaterialSchema) => MaterialSchema | undefined;
|
addMaterial: (material: MaterialSchema) => MaterialSchema | undefined;
|
||||||
removeMaterial: (materialId: string) => MaterialSchema | undefined;
|
removeMaterial: (materialId: string) => MaterialSchema | undefined;
|
||||||
|
clearMaterials: () => void;
|
||||||
updateMaterial: (materialId: string, updates: Partial<MaterialSchema>) => MaterialSchema | undefined;
|
updateMaterial: (materialId: string, updates: Partial<MaterialSchema>) => MaterialSchema | undefined;
|
||||||
|
|
||||||
setCurrentLocation: (
|
setCurrentLocation: (
|
||||||
@@ -35,6 +36,7 @@ type MaterialsStore = {
|
|||||||
setIsRendered: (materialId: string, isRendered: boolean) => MaterialSchema | undefined;
|
setIsRendered: (materialId: string, isRendered: boolean) => MaterialSchema | undefined;
|
||||||
|
|
||||||
getMaterialById: (materialId: string) => MaterialSchema | undefined;
|
getMaterialById: (materialId: string) => MaterialSchema | undefined;
|
||||||
|
getMaterialByCurrentModelUuid: (currentModelUuid: string) => MaterialSchema | undefined;
|
||||||
getMaterialsByPoint: (pointUuid: string) => MaterialSchema[];
|
getMaterialsByPoint: (pointUuid: string) => MaterialSchema[];
|
||||||
getMaterialsByModel: (modelUuid: string) => MaterialSchema[];
|
getMaterialsByModel: (modelUuid: string) => MaterialSchema[];
|
||||||
};
|
};
|
||||||
@@ -63,6 +65,12 @@ export const useMaterialStore = create<MaterialsStore>()(
|
|||||||
return updatedMaterial;
|
return updatedMaterial;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
clearMaterials: () => {
|
||||||
|
set((state) => {
|
||||||
|
state.materials = [];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
updateMaterial: (materialId, updates) => {
|
updateMaterial: (materialId, updates) => {
|
||||||
let updatedMaterial: MaterialSchema | undefined;
|
let updatedMaterial: MaterialSchema | undefined;
|
||||||
set((state) => {
|
set((state) => {
|
||||||
@@ -186,6 +194,10 @@ export const useMaterialStore = create<MaterialsStore>()(
|
|||||||
getMaterialById: (materialId) => {
|
getMaterialById: (materialId) => {
|
||||||
return get().materials.find(m => m.materialId === materialId);
|
return get().materials.find(m => m.materialId === materialId);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getMaterialByCurrentModelUuid: (currentModelUuid) => {
|
||||||
|
return get().materials.find(m => m.current?.modelUuid === currentModelUuid);
|
||||||
|
},
|
||||||
|
|
||||||
getMaterialsByPoint: (pointUuid) => {
|
getMaterialsByPoint: (pointUuid) => {
|
||||||
return get().materials.filter(m =>
|
return get().materials.filter(m =>
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ type ProductsStore = {
|
|||||||
// Helper functions
|
// Helper functions
|
||||||
getProductById: (productId: string) => { productName: string; productId: string; eventDatas: EventsSchema[] } | undefined;
|
getProductById: (productId: string) => { productName: string; productId: string; eventDatas: EventsSchema[] } | undefined;
|
||||||
getEventByModelUuid: (productId: string, modelUuid: string) => EventsSchema | undefined;
|
getEventByModelUuid: (productId: string, modelUuid: string) => EventsSchema | undefined;
|
||||||
|
getEventByTriggerUuid: (productId: string, triggerUuid: string) => EventsSchema | undefined;
|
||||||
|
getEventByPointUuid: (productId: string, pointUuid: string) => EventsSchema | undefined;
|
||||||
getPointByUuid: (productId: string, modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined;
|
getPointByUuid: (productId: string, modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined;
|
||||||
getActionByUuid: (productId: string, actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined;
|
getActionByUuid: (productId: string, actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined;
|
||||||
getModelUuidByPointUuid: (productId: string, actionUuid: string) => (string) | undefined;
|
getModelUuidByPointUuid: (productId: string, actionUuid: string) => (string) | undefined;
|
||||||
@@ -540,6 +542,53 @@ export const useProductStore = create<ProductsStore>()(
|
|||||||
return product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
|
return product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getEventByTriggerUuid: (productId, triggerUuid) => {
|
||||||
|
const product = get().getProductById(productId);
|
||||||
|
if (!product) return undefined;
|
||||||
|
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
if ('points' in event) {
|
||||||
|
for (const point of (event as ConveyorEventSchema).points) {
|
||||||
|
if (point.action?.triggers?.some(t => t.triggerUuid === triggerUuid)) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
const point = (event as any).point;
|
||||||
|
if ('action' in point) {
|
||||||
|
if (point.action?.triggers?.some((t: any) => t.triggerUuid === triggerUuid)) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
} else if ('actions' in point) {
|
||||||
|
for (const action of point.actions) {
|
||||||
|
if (action.triggers?.some((t: any) => t.triggerUuid === triggerUuid)) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
getEventByPointUuid: (productId, pointUuid) => {
|
||||||
|
const product = get().getProductById(productId);
|
||||||
|
if (!product) return undefined;
|
||||||
|
|
||||||
|
for (const event of product.eventDatas) {
|
||||||
|
if ('points' in event) {
|
||||||
|
if ((event as ConveyorEventSchema).points.some(p => p.uuid === pointUuid)) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
} else if ('point' in event) {
|
||||||
|
if ((event as any).point?.uuid === pointUuid) {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
|
||||||
getPointByUuid: (productId, modelUuid, pointUuid) => {
|
getPointByUuid: (productId, modelUuid, pointUuid) => {
|
||||||
const event = get().getEventByModelUuid(productId, modelUuid);
|
const event = get().getEventByModelUuid(productId, modelUuid);
|
||||||
if (!event) return undefined;
|
if (!event) return undefined;
|
||||||
|
|||||||
Reference in New Issue
Block a user