@@ -1,11 +1,12 @@
import { useFrame , useThree } from "@react-three/fiber" ;
import React , { useEffect , useRef , useState } from "react" ;
import React , { useEffect , useMemo , useRef , useState } from "react" ;
import { Quaternion , Vector3 } from "three" ;
import {
useAnimationPlaySpeed ,
usePlayButtonStore ,
} from "../../../../../store/usePlayButtonStore" ;
import { usePathManager } from "../../pathCreator/function/usePathManager" ;
import { useCreatedPaths } from "../../../../../store/builder/store" ;
interface VehicleAnimatorProps {
vehiclesData : VehicleStructure [ ] ;
@@ -21,6 +22,7 @@ export default function VehicleAnimator2({
const { scene } = useThree ( ) ;
const { speed } = useAnimationPlaySpeed ( ) ;
const { isPlaying } = usePlayButtonStore ( ) ;
const { paths , allPaths , setAllPaths } = useCreatedPaths ( ) ;
const vehicleMovementState = useRef <
Record <
string ,
@@ -34,138 +36,33 @@ export default function VehicleAnimator2({
>
> ( { } ) ;
const managerRef = useRef < ManagerData > ( ) ;
// const manager = usePathManager(managerData?.pathId, managerData?.vehicleId);
// Initialize all paths into allPaths store
useEffect ( ( ) = > {
if ( ! paths || paths . length === 0 ) return ;
// useFrame((_, delta) => {
// if (!isPlaying) return;
const newPaths = useCreatedPaths . getState ( ) . paths . map ( ( val : any ) = > ( {
pathId : val.pathId ,
isAvailable : true ,
vehicleId : null ,
} ) ) ;
const merged = [ . . . useCreatedPaths . getState ( ) . allPaths ] ;
newPaths . forEach ( ( p : any ) = > {
if ( ! merged . find ( ( m ) = > m . pathId === p . pathId ) ) {
merged . push ( p ) ;
}
} ) ;
// vehiclesData.forEach((vehicle) => {
// const { vehicleId, route } = vehicle;
// if (
// !route ||
// route.length === 0 ||
// !route[0].pathPoints ||
// route[0].pathPoints.length < 2
// ) {
// return;
// }
// const mesh = scene.getObjectByProperty("uuid", vehicleId); // or getObjectByName
// if (!mesh) return;
// const currentPath = route[0];
// const pathPoints = currentPath.pathPoints;
// const currentPathId = currentPath.pathId;
// // ✅ Call your function here
// setManagerData({ pathId: currentPathId, vehicleId: vehicleId });
// const pathStart = new Vector3(...pathPoints[0].position);
// const uuid = vehicleId;
// // Initialize movement state
// if (!vehicleMovementState.current[uuid]) {
// vehicleMovementState.current[uuid] = {
// index: 0,
// progress: 0,
// hasStarted: false,
// };
// }
// const state = vehicleMovementState.current[uuid];
// // ✳️ Step 1: Move to start point first
// if (!state.hasStarted) {
// const distanceToStart = mesh.position.distanceTo(pathStart);
// const step = speed * delta;
// if (distanceToStart <= step) {
// mesh.position.copy(pathStart);
// mesh.quaternion.identity();
// state.hasStarted = true;
// return;
// }
// const direction = pathStart.clone().sub(mesh.position);
// const distance = direction.length();
// if (distance > step) {
// direction.normalize();
// mesh.position.add(direction.clone().multiplyScalar(step));
// const forward = new Vector3(0, 0, 1);
// const targetQuat = new Quaternion().setFromUnitVectors(
// forward,
// direction
// );
// mesh.quaternion.slerp(targetQuat, 0.1);
// } else {
// mesh.position.copy(pathStart);
// mesh.quaternion.identity();
// state.hasStarted = true;
// state.index = 0;
// state.progress = 0;
// }
// return;
// }
// // ✳️ Step 2: Move along the route
// const currentPoint = new Vector3(...pathPoints[state.index].position);
// const nextPoint = new Vector3(...pathPoints[state.index + 1].position);
// const segmentVector = new Vector3().subVectors(nextPoint, currentPoint);
// const segmentLength = segmentVector.length();
// const direction = segmentVector.clone().normalize();
// const moveDistance = speed * delta;
// state.progress += moveDistance / segmentLength;
// if (state.progress >= 1) {
// state.index++;
// state.progress = 0;
// if (state.index >= pathPoints.length - 1) {
// // Stop at the end
// state.index = pathPoints.length - 2;
// state.progress = 1;
// }
// }
// // Interpolate position
// const newPos = new Vector3().lerpVectors(
// currentPoint,
// nextPoint,
// state.progress
// );
// mesh.position.copy(newPos);
// // Smooth rotation
// const forward = new Vector3(0, 0, 1);
// const targetQuat = new Quaternion().setFromUnitVectors(
// forward,
// direction
// );
// mesh.quaternion.slerp(targetQuat, 0.1);
// });
// });
// ✅ Shared waypoint reservation system
const reservedWaypoints = new Set < string > ( ) ;
function tryReserveWaypoint ( vehicleId : string , waypointId : string ) {
if ( reservedWaypoints . has ( waypointId ) ) return false ; // already reserved
reservedWaypoints . add ( waypointId ) ;
return true ;
}
function releaseWaypoint ( waypointId : string ) {
reservedWaypoints . delete ( waypointId ) ;
}
if ( merged . length !== useCreatedPaths . getState ( ) . allPaths . length ) {
setAllPaths ( merged ) ;
}
} , [ paths , allPaths , setAllPaths ] ) ;
useFrame ( ( _ , delta ) = > {
if ( ! isPlaying ) return ;
vehiclesData . forEach ( ( vehicle ) = > {
const { vehicleId , route } = vehicle ;
if ( ! route || route . length === 0 ) return ;
const mesh = scene . getObjectByProperty ( "uuid" , vehicleId ) ;
@@ -173,7 +70,7 @@ export default function VehicleAnimator2({
const uuid = vehicleId ;
// ✅ Initialize state
// ✅ Initialize state if not already
if ( ! vehicleMovementState . current [ uuid ] ) {
vehicleMovementState . current [ uuid ] = {
index : 0 ,
@@ -196,7 +93,9 @@ export default function VehicleAnimator2({
let pathPoints = currentPath . pathPoints ;
const pathStart = new Vector3 ( . . . pathPoints [ 0 ] . position ) ;
// Step 1: move mesh to start
/**
* 🟢 STEP 1: Move vehicle to the starting point of its first path
*/
if ( ! state . hasStarted ) {
const distanceToStart = mesh . position . distanceTo ( pathStart ) ;
const step = speed * delta ;
@@ -221,14 +120,9 @@ export default function VehicleAnimator2({
return ;
}
// ✅ Conflict Resolution: Check reservation before moving
const nextWaypointId = ` ${ currentPath . pathId } - ${ state . pointIndex + 1 } ` ;
if ( ! tryReserveWaypoint ( uuid , nextWaypointId ) ) {
// ⏸ Wait here until waypoint becomes free
return ;
}
// Step 2: Move along current path
/**
* 🟢 STEP 2: Move along path once at start
*/
const currentPoint = new Vector3 (
. . . pathPoints [ state . pointIndex ] . position
) ;
@@ -244,30 +138,19 @@ export default function VehicleAnimator2({
state . progress += moveDistance / segmentLength ;
if ( state . progress >= 1 ) {
// ✅ Release waypoint once passed
releaseWaypoint ( nextWaypointId ) ;
state . pointIndex ++ ;
state . progress = 0 ;
// ✅ reached end of current path → switch to next path
if ( state . pointIndex >= pathPoints . length - 1 ) {
state . pathIndex ++ ;
state . pointIndex = 0 ;
state . progress = 0 ;
// 🔥 refresh path data after advancing
if ( state . pathIndex < route . length ) {
currentPath = route [ state . pathIndex ] ;
pathPoints = currentPath . pathPoints ;
} else {
// ✅ Finished ALL paths → stop
state . pathIndex = route . length - 1 ;
const lastPath = route [ state . pathIndex ] ;
const lastPts = lastPath . pathPoints ;
state . pointIndex = lastPts . length - 2 ;
state . progress = 1 ;
if ( state . pathIndex >= route . length ) {
return ;
}
currentPath = route [ state . pathIndex ] ;
pathPoints = currentPath . pathPoints ;
}
}
@@ -286,151 +169,129 @@ export default function VehicleAnimator2({
direction
) ;
mesh . quaternion . slerp ( targetQuat , 0.1 ) ;
const pathCheck = handlePathCheck ( currentPath . pathId , vehicleId ) ;
console . log ( "pathCheck: " , pathCheck ) ;
//
// 🟢 Log current pathId while moving
// console.log(
// `🚗 Vehicle ${uuid} moving on pathId: ${currentPath.pathId}`,
// "→",
// newPos.toArray()
// );
} ) ;
} ) ;
// const handlePathCheck = (pathId: string, vehicleId: string) => {
// const { paths, allPaths, setAllPaths } = useCreatedPaths.getState();
// useFrame((_, delta) => {
// if (!isPlaying) return;
// const normalize = (v: any) => String(v ?? "").trim();
// vehiclesData.forEach((vehicle) => {
// console.log("vehiclesData: ", vehiclesData);
// const { vehicleId, route } = vehicle;
// if (!route || route.length === 0) return ;
// // Find path
// const path = paths.find(
// (p: any) => normalize(p.pathId) === normalize(pathId)
// ) ;
// if (!path) {
//
// return false;
// }
// const mesh = scene.getObjectByProperty("uuid", vehicleId);
// if (!mesh) return;
// const uuid = vehicleId;
// // ✅ Initialize state
// if (!vehicleMovementState.current[uuid]) {
// vehicleMovementState.current[uuid] = {
// index: 0,
// pointIndex: 0,
// pathIndex: 0,
// progress: 0,
// hasStarted: false,
// };
// }
// const state = vehicleMovementState.current[uuid];
// let currentPath = route[state.pathIndex];
// if (
// !currentPath ||
// !currentPath.pathPoints ||
// currentPath.pathPoints.length < 2
// )
// return;
// let pathPoints = currentPath.pathPoints;
// const pathStart = new Vector3(...pathPoints[0].position);
// // Step 1: move mesh to start
// if (!state.hasStarted) {
// const distanceToStart = mesh.position.distanceTo(pathStart);
// const step = speed * delta;
// if (distanceToStart <= step) {
// mesh.position.copy(pathStart);
// mesh.quaternion.identity();
// state.hasStarted = true;
// return;
// }
// const direction = pathStart.clone().sub(mesh.position).normalize();
// mesh.position.add(direction.clone().multiplyScalar(step));
// const forward = new Vector3(0, 0, 1);
// const targetQuat = new Quaternion().setFromUnitVectors(
// forward,
// direction
// );
// mesh.quaternion.slerp(targetQuat, 0.1);
// return;
// }
// // managerRef.current = { pathId: currentPath.pathId, vehicleId: uuid };
// // // ✅ only setState when pathId actually changes
// // if (managerData?.pathId !== currentPath.pathId) {
// // setManagerData({ pathId: currentPath.pathId, vehicleId: uuid });
// // }
// if (
// !managerRef.current ||
// managerRef.current.pathId !== currentPath.pathId ||
// managerRef.current.vehicleId !== uuid
// ) {
// managerRef.current = { pathId: currentPath.pathId, vehicleId: uuid };
// }
// // Step 2: Move along current path
// const currentPoint = new Vector3(
// ...pathPoints[state.pointIndex].position
// );
// const nextPoint = new Vector3(
// ...pathPoints[state.pointIndex + 1].position
// // If path already reserved → always reject
// if (!path.isAvailable) {
// console.log(
// `🚨 Path ${pathId} is already reserved by vehicle ${ vehicleId}`
// );
// return false;
// }
//
// const segmentVector = new Vector3().subVectors(nextPoint, currentPoint);
// const segmentLength = segmentVector.length();
// const direction = segmentVector.clone(). normalize();
// // Reserve the path for this vehicle
// const updated = allPaths.map((p: any) =>
// normalize(p.pathId) === normalize(pathId )
// ? { ...p, vehicleId, isAvailable: false }
// : p
// );
// console.log("speed: ", spe ed);
// const moveDistance = speed * delta;
// state.progress += moveDistance / segmentLength;
// setAllPaths(updat ed);
// if (state.progress >= 1) {
// state.pointIndex++ ;
// state.progress = 0 ;
//
// return true ;
// } ;
// // ✅ reached end of current path → switch to next path
// if (state.pointIndex >= pathPoints.length - 1) {
// state.pathIndex++;
// state.pointIndex = 0;
// state.progress = 0;
const handlePathCheck = ( pathId : string , vehicleId : string ) = > {
const normalize = ( v : any ) = > String ( v ? ? "" ) . trim ( ) ;
// // 🔥 refresh path data after advancing
// if (state.pathIndex < route.length) {
// currentPath = route[state.pathIndex];
// pathPoints = currentPath.pathPoints;
// } else {
// // ✅ Finished ALL paths → stop
// state.pathIndex = route.length - 1;
// const lastPath = route[state.pathIndex];
// const lastPts = lastPath.pathPoints;
// state.pointIndex = lastPts.length - 2;
// state.progress = 1;
// }
// }
// }
// Find path
const path = useCreatedPaths
. getState ( )
. allPaths . find ( ( p : any ) = > normalize ( p . pathId ) === normalize ( pathId ) ) ;
// // Interpolate position
// const newPos = new Vector3().lerpVectors(
// new Vector3(...pathPoints[state.pointIndex].position),
// new Vector3(...pathPoints[state.pointIndex + 1].position),
// state.progress
// );
// mesh.position.copy(newPos);
if ( ! path ) {
return false ;
}
// // Smooth rotation
// const forward = new Vector3(0, 0, 1);
// const targetQuat = new Quaternion().setFromUnitVectors(
// for ward,
// direction
// );
// mesh.quaternion.slerp(targetQuat, 0.1);
// });
// });
// If path already reserved → reject always
if ( ! path . isAvailable ) {
// console. warn(
// `🚨 Path ${pathId} is already reserved by vehicle ${vehicleId}`
// );
echo . warn ( ` Path ${ pathId } ` ) ;
return false ;
} else {
console . log ( "path is reserved" ) ;
}
// Reserve the path properly with vehicleId
const updated = allPaths . map ( ( p : any ) = >
normalize ( p . pathId ) === normalize ( pathId )
? { . . . p , vehicleId , isAvailable : false }
: p
) ;
console . log ( "updated: " , updated ) ;
setAllPaths ( updated ) ;
return true ;
} ;
// const manager = usePathManager(
// managerRef.current?.pathId,
// managerRef.current?.vehicleId
// );
// useEffect(() => {
// console.log("managerRef.current: ", managerRef.current);
// if (managerRef.current) {
// console.log("Path manager updated:", manager);
// }
// }, [manager, managerRef.current]);
return null ;
}
// const handlePathCheck = (pathId: string, vehicleId: string) => {
// const allPaths = useCreatedPaths.getState().allPaths;
// // find the path we’ re checking
// const path = allPaths.find((p: any) => p.pathId === pathId);
// if (!path) {
//
// return false;
// }
// // if path is available, update it with vehicleId and mark unavailable
// if (path.isAvailable) {
// const updated = allPaths.map((p: any) =>
// p.pathId === pathId ? { ...p, vehicleId, isAvailable: false } : p
// );
//
// //
// // setAllPaths(updated); // uncomment if you want to persist update
// return true; // path was available
// }
//
// return false; // path not available
// };
// useEffect(() => {
//
// if (managerRef.current) {
//
// }
// }, [manager, managerRef.current]);