Merge remote-tracking branch 'origin/main-dev' into feature/agv-edit

This commit is contained in:
2025-07-05 11:20:50 +05:30
68 changed files with 3868 additions and 860 deletions

View File

@@ -3,12 +3,7 @@ import { useThree, useFrame } from '@react-three/fiber';
import * as THREE from 'three';
import { MaterialModel } from '../../../materials/instances/material/materialModel';
type MaterialAnimatorProps = {
agvDetail: VehicleStatus;
};
const MaterialAnimator = ({ agvDetail }: MaterialAnimatorProps) => {
const MaterialAnimator = ({ agvDetail }: { agvDetail: VehicleStatus }) => {
const meshRef = useRef<any>(null!);
const [hasLoad, setHasLoad] = useState(false);
const { scene } = useThree();

View File

@@ -71,7 +71,6 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
}
}, [isReset, isPlaying])
const lastTimeRef = useRef(performance.now());
useFrame(() => {
@@ -283,88 +282,6 @@ function DraggableSphere({
);
}
// function DraggableLineSegment({
// index,
// start,
// end,
// updatePoints,
// isAnyDragging,
// setIsAnyDragging,
// }: {
// index: number;
// start: THREE.Vector3;
// end: THREE.Vector3;
// updatePoints: (i0: number, p0: THREE.Vector3, i1: number, p1: THREE.Vector3) => void;
// isAnyDragging: string;
// setIsAnyDragging: (val: string) => void;
// }) {
// const meshRef = useRef<THREE.Mesh>(null);
// const { gl, raycaster, controls } = useThree();
// const { activeTool } = useActiveTool();
// const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
// const dragStart = useRef<THREE.Vector3 | null>(null);
// const onPointerDown = () => {
// if (activeTool !== 'pen' || isAnyDragging) return; // <-- Skip if dragging sphere
// setIsAnyDragging("line");
// gl.domElement.style.cursor = 'grabbing';
// if (controls) (controls as any).enabled = false;
// };
// const onPointerMove = (e: ThreeEvent<PointerEvent>) => {
// if (isAnyDragging !== "line" || activeTool !== 'pen') return;
// const intersect = new THREE.Vector3();
// if (raycaster.ray.intersectPlane(plane, intersect)) {
// if (!dragStart.current) dragStart.current = intersect.clone();
// const offset = new THREE.Vector3().subVectors(intersect, dragStart.current);
// const newStart = start.clone().add(offset);
// const newEnd = end.clone().add(offset);
// updatePoints(index, newStart, index + 1, newEnd);
// }
// };
// const onPointerUp = () => {
// if (activeTool !== 'pen') return;
// setIsAnyDragging("");
// dragStart.current = null;
// gl.domElement.style.cursor = 'default';
// if (controls) (controls as any).enabled = true;
// };
// const noopRaycast = (raycaster: THREE.Raycaster, intersects: THREE.Intersection[]) => { };
// return (
// <mesh
// ref={meshRef}
// onPointerDown={onPointerDown}
// onPointerMove={onPointerMove}
// onPointerUp={onPointerUp}
// onPointerMissed={onPointerUp}
// raycast={isAnyDragging === "point" ? noopRaycast : undefined} // ✅ Safe
// >
// <Line points={[start, end]} color="blue" lineWidth={5} />
// </mesh>
// );
// // return (
// // <mesh
// // ref={meshRef}
// // onPointerDown={onPointerDown}
// // onPointerMove={onPointerMove}
// // onPointerUp={onPointerUp}
// // onPointerMissed={onPointerUp}
// // // raycast={isAnyDragging === 'point' ? () => null : undefined}
// // raycast={isAnyDragging === "point" ? () => { } : undefined}
// // // pointerEvents={isAnyDragging === "point" ? "none" : "auto"} // ✅ the correct way
// // >
// // <Line points={[start, end]} color="blue" lineWidth={5} />
// // </mesh>
// // );
// }
function DraggableLineSegment({
index,
start,
@@ -426,207 +343,3 @@ function DraggableLineSegment({
/>
);
}
// These are recently edited files. Do not suggest code that has been deleted.
// function DraggableSphere({
// index,
// position,
// onMove,
// isAnyDragging,
// setIsAnyDragging,
// }: {
// index: number;
// position: THREE.Vector3;
// onMove: (index: number, pos: THREE.Vector3) => void;
// isAnyDragging: boolean;
// setIsAnyDragging: (val: boolean) => void;
// }) {
// const meshRef = useRef<THREE.Mesh>(null);
// const { gl, controls, raycaster } = useThree();
// const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); // XZ plane
// const isDragging = useRef(false);
// const { activeTool } = useActiveTool();
// const onPointerDown = (e: ThreeEvent<PointerEvent>) => {
// if (activeTool !== 'pen') return;
// isDragging.current = true;
// gl.domElement.style.cursor = 'grabbing';
// if (controls) {
// (controls as any).enabled = false;
// }
// };
// const onPointerMove = (e: ThreeEvent<PointerEvent>) => {
// if (!isDragging.current || activeTool !== 'pen') return;
// const intersect = new THREE.Vector3();
// if (raycaster.ray.intersectPlane(plane, intersect)) {
// meshRef.current!.position.copy(intersect);
// onMove(index, intersect);
// }
// };
// const onPointerUp = () => {
// if (activeTool !== 'pen') return;
// isDragging.current = false;
// gl.domElement.style.cursor = 'default';
// if (controls) {
// (controls as any).enabled = true;
// }
// };
// return (
// <mesh
// ref={meshRef}
// position={position}
// onPointerDown={onPointerDown}
// onPointerMove={onPointerMove}
// onPointerUp={onPointerUp}
// onPointerMissed={onPointerUp}
// >
// <sphereGeometry args={[0.2, 16, 16]} />
// <meshStandardMaterial color="red" />
// </mesh>
// );
// }
// function DraggableLineSegment({
// index,
// start,
// end,
// updatePoints,
// isAnyDragging,
// setIsAnyDragging,
// }: {
// index: number;
// start: THREE.Vector3;
// end: THREE.Vector3;
// updatePoints: (i0: number, p0: THREE.Vector3, i1: number, p1: THREE.Vector3) => void;
// isAnyDragging: boolean;
// setIsAnyDragging: (val: boolean) => void;
// }) {
// const meshRef = useRef<THREE.Mesh>(null);
// const { gl, camera, controls, raycaster } = useThree();
// const { activeTool } = useActiveTool(); // 👈 Get active tool
// const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
// const isDragging = useRef(false);
// const dragStart = useRef<THREE.Vector3 | null>(null);
// const onPointerDown = () => {
// if (activeTool !== 'pen') return; // 👈 Only allow when tool is 'pen'
// isDragging.current = true;
// gl.domElement.style.cursor = 'grabbing';
// if (controls) (controls as any).enabled = false;
// };
// const onPointerMove = (e: ThreeEvent<PointerEvent>) => {
// if (!isDragging.current || activeTool !== 'pen') return;
// const intersect = new THREE.Vector3();
// if (raycaster.ray.intersectPlane(plane, intersect)) {
// if (!dragStart.current) {
// dragStart.current = intersect.clone();
// }
// const offset = new THREE.Vector3().subVectors(intersect, dragStart.current);
// const newStart = start.clone().add(offset);
// const newEnd = end.clone().add(offset);
// updatePoints(index, newStart, index + 1, newEnd);
// }
// };
// const onPointerUp = () => {
// if (activeTool !== 'pen') return;
// isDragging.current = false;
// dragStart.current = null;
// gl.domElement.style.cursor = 'default';
// if (controls) (controls as any).enabled = true;
// };
// return (
// <mesh
// ref={meshRef}
// onPointerDown={onPointerDown}
// onPointerMove={onPointerMove}
// onPointerUp={onPointerUp}
// onPointerMissed={onPointerUp}
// >
// <Line points={[start, end]} color="blue" lineWidth={10} />
// </mesh>
// );
// }
// return (
// <>
// {selectedPath === "auto" && <group>
// {/* {currentPath.map((pos, i) => {
// if (i < currentPath.length - 1) {
// return (
// <DraggableLineSegment
// key={i}
// index={i}
// start={new THREE.Vector3(...currentPath[i])}
// end={new THREE.Vector3(...currentPath[i + 1])}
// updatePoints={(i0, p0, i1, p1) => {
// const updated = [...currentPath];
// updated[i0] = p0.toArray() as [number, number, number];
// updated[i1] = p1.toArray() as [number, number, number];
// setCurrentPath(updated);
// }}
// />
// );
// }
// return null;
// })} */}
// {currentPath.length > 0 && (
// <group onPointerMissed={() => { if (controls) (controls as any).enabled = true; }}>
// <Line points={currentPath} color="blue" lineWidth={3} />
// {currentPath.map((pos, i) => {
// if (i < currentPath.length - 1) {
// return (
// <React.Fragment key={i}>
// <DraggableSphere
// key={i}
// index={i}
// position={new THREE.Vector3(...pos)}
// onMove={updatePoint}
// />
// {/* <DraggableLineSegment
// key={i}
// index={i}
// start={new THREE.Vector3(...currentPath[i])}
// end={new THREE.Vector3(...currentPath[i + 1])}
// updatePoints={(i0, p0, i1, p1) => {
// const updated = [...currentPath];
// updated[i0] = p0.toArray() as [number, number, number];
// updated[i1] = p1.toArray() as [number, number, number];
// setCurrentPath(updated);
// }}
// /> */}
// </React.Fragment>
// )
// }
// }
// )}
// </group >
// )
// }
// </group >
// }
// </>
// );

View File

@@ -1,21 +1,23 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import VehicleAnimator from '../animator/vehicleAnimator';
import * as THREE from 'three';
import { NavMeshQuery } from '@recast-navigation/core';
import { useNavMesh, useSelectedPath } from '../../../../../store/builder/store';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
import MaterialAnimator from '../animator/materialAnimator';
import { useSceneContext } from '../../../../scene/sceneContext';
import { useProductContext } from '../../../products/productContext';
import InteractivePoints from '../animator/interactivePoint';
import MaterialAnimator from '../animator/materialAnimator';
import VehicleAnimator from '../animator/vehicleAnimator';
function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) {
const { navMesh } = useNavMesh();
const { isPlaying } = usePlayButtonStore();
const { materialStore, armBotStore, conveyorStore, vehicleStore, storageUnitStore, productStore } = useSceneContext();
const { materialStore, armBotStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, productStore } = useSceneContext();
const { removeMaterial, setEndTime } = materialStore();
const { getStorageUnitById } = storageUnitStore();
const { getHumanById } = humanStore();
const { getArmBotById } = armBotStore();
const { getConveyorById } = conveyorStore();
const { triggerPointActions } = useTriggerHandler();
@@ -149,7 +151,6 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
}
}, [vehicles, currentPhase, path, isPlaying, selectedPath]);
function animate(currentTime: number) {
if (previousTimeRef.current === null) {
previousTimeRef.current = currentTime;
@@ -196,7 +197,6 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
};
}, [agvDetail, isPlaying]);
function handleCallBack() {
if (currentPhase === 'stationed-pickup') {
setCurrentPhase('picking');
@@ -246,6 +246,11 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
if (action) {
handleMaterialDropToStorageUnit(model);
}
} else if (model.type === 'human') {
const action = getActionByUuid(selectedProduct.productUuid, agvDetail.point.action.actionUuid);
if (action) {
handleMaterialDropToHuman(model);
}
}
} else {
const droppedMaterial = agvDetail.currentLoad;
@@ -259,6 +264,79 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
}
}
function handleMaterialDropToHuman(model: HumanEventSchema) {
if (model) {
if (model.point.action.actionType === 'worker') {
loopMaterialDropToHuman(
agvDetail.modelUuid,
agvDetail.currentLoad,
agvDetail.point.action.unLoadDuration,
model.modelUuid,
model.point.action.loadCapacity,
agvDetail.point.action
);
}
}
}
function loopMaterialDropToHuman(
vehicleId: string,
vehicleCurrentLoad: number,
unLoadDuration: number,
humanId: string,
storageMaxCapacity: number,
action: VehicleAction
) {
startTime = performance.now();
const fixedInterval = ((unLoadDuration / vehicleCurrentLoad) * (1000 / isSpeedRef.current));
const unloadLoop = () => {
if (isPausedRef.current) {
pauseTimeRef.current ??= performance.now();
requestAnimationFrame(unloadLoop);
return;
}
if (pauseTimeRef.current) {
const pauseDuration = performance.now() - pauseTimeRef.current;
startTime += pauseDuration;
pauseTimeRef.current = null;
}
const elapsedTime = performance.now() - startTime;
const human = getHumanById(humanId);
if (elapsedTime >= fixedInterval) {
if (human && agvDetail &&
human.currentLoad < storageMaxCapacity &&
vehicleCurrentLoad > 0) {
decrementVehicleLoad(vehicleId, 1);
vehicleCurrentLoad -= 1;
const material = removeLastMaterial(vehicleId);
if (material) {
triggerPointActions(action, material.materialId);
}
if (vehicleCurrentLoad > 0 && human.currentLoad < storageMaxCapacity) {
startTime = performance.now();
requestAnimationFrame(unloadLoop);
}
}
} else {
requestAnimationFrame(unloadLoop);
}
};
const human = getHumanById(humanId);
if (human && vehicleCurrentLoad > 0 && human?.currentLoad < storageMaxCapacity) {
unloadLoop();
}
}
function handleMaterialDropToStorageUnit(model: StorageEventSchema) {
if (model) {
if (model.point.action.actionType === 'store') {
@@ -527,10 +605,4 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
);
}
export default VehicleInstance;
export default VehicleInstance;

View File

@@ -5,20 +5,20 @@ import { useSceneContext } from "../../../scene/sceneContext";
import { useViewSceneStore } from "../../../../store/builder/store";
function VehicleInstances() {
const { vehicleStore } = useSceneContext();
const { vehicles } = vehicleStore();
const { viewSceneLabels } = useViewSceneStore();
const { vehicleStore } = useSceneContext();
const { vehicles } = vehicleStore();
const { viewSceneLabels } = useViewSceneStore();
return (
<>
{vehicles.map((vehicle: VehicleStatus) => (
<React.Fragment key={vehicle.modelUuid}>
<VehicleInstance agvDetail={vehicle} />
{viewSceneLabels && <VehicleContentUi vehicle={vehicle} />}
</React.Fragment>
))}
</>
);
return (
<>
{vehicles.map((vehicle: VehicleStatus) => (
<React.Fragment key={vehicle.modelUuid}>
<VehicleInstance agvDetail={vehicle} />
{viewSceneLabels && <VehicleContentUi vehicle={vehicle} />}
</React.Fragment>
))}
</>
);
}
export default VehicleInstances;