added vehicle conflict path structure

This commit is contained in:
2025-09-02 09:13:06 +05:30
parent 6da82895b7
commit c60f15db13
4 changed files with 141 additions and 289 deletions

View File

@@ -1,11 +1,12 @@
import { useFrame, useThree } from "@react-three/fiber"; 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 { Quaternion, Vector3 } from "three";
import { import {
useAnimationPlaySpeed, useAnimationPlaySpeed,
usePlayButtonStore, usePlayButtonStore,
} from "../../../../../store/usePlayButtonStore"; } from "../../../../../store/usePlayButtonStore";
import { usePathManager } from "../../pathCreator/function/usePathManager"; import { usePathManager } from "../../pathCreator/function/usePathManager";
import { useCreatedPaths } from "../../../../../store/builder/store";
interface VehicleAnimatorProps { interface VehicleAnimatorProps {
vehiclesData: VehicleStructure[]; vehiclesData: VehicleStructure[];
@@ -21,6 +22,7 @@ export default function VehicleAnimator2({
const { scene } = useThree(); const { scene } = useThree();
const { speed } = useAnimationPlaySpeed(); const { speed } = useAnimationPlaySpeed();
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { paths, allPaths, setAllPaths } = useCreatedPaths();
const vehicleMovementState = useRef< const vehicleMovementState = useRef<
Record< Record<
string, string,
@@ -34,138 +36,33 @@ export default function VehicleAnimator2({
> >
>({}); >({});
const managerRef = useRef<ManagerData>(); 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) => { const newPaths = useCreatedPaths.getState().paths.map((val: any) => ({
// if (!isPlaying) return; 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) => { if (merged.length !== useCreatedPaths.getState().allPaths.length) {
// const { vehicleId, route } = vehicle; setAllPaths(merged);
}
// if ( }, [paths, allPaths, setAllPaths]);
// !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);
}
useFrame((_, delta) => { useFrame((_, delta) => {
if (!isPlaying) return; if (!isPlaying) return;
vehiclesData.forEach((vehicle) => { vehiclesData.forEach((vehicle) => {
const { vehicleId, route } = vehicle; const { vehicleId, route } = vehicle;
if (!route || route.length === 0) return; if (!route || route.length === 0) return;
const mesh = scene.getObjectByProperty("uuid", vehicleId); const mesh = scene.getObjectByProperty("uuid", vehicleId);
@@ -173,7 +70,7 @@ export default function VehicleAnimator2({
const uuid = vehicleId; const uuid = vehicleId;
// ✅ Initialize state // ✅ Initialize state if not already
if (!vehicleMovementState.current[uuid]) { if (!vehicleMovementState.current[uuid]) {
vehicleMovementState.current[uuid] = { vehicleMovementState.current[uuid] = {
index: 0, index: 0,
@@ -196,7 +93,9 @@ export default function VehicleAnimator2({
let pathPoints = currentPath.pathPoints; let pathPoints = currentPath.pathPoints;
const pathStart = new Vector3(...pathPoints[0].position); 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) { if (!state.hasStarted) {
const distanceToStart = mesh.position.distanceTo(pathStart); const distanceToStart = mesh.position.distanceTo(pathStart);
const step = speed * delta; const step = speed * delta;
@@ -221,14 +120,9 @@ export default function VehicleAnimator2({
return; return;
} }
// ✅ Conflict Resolution: Check reservation before moving /**
const nextWaypointId = `${currentPath.pathId}-${state.pointIndex + 1}`; * 🟢 STEP 2: Move along path once at start
if (!tryReserveWaypoint(uuid, nextWaypointId)) { */
// ⏸ Wait here until waypoint becomes free
return;
}
// Step 2: Move along current path
const currentPoint = new Vector3( const currentPoint = new Vector3(
...pathPoints[state.pointIndex].position ...pathPoints[state.pointIndex].position
); );
@@ -244,30 +138,19 @@ export default function VehicleAnimator2({
state.progress += moveDistance / segmentLength; state.progress += moveDistance / segmentLength;
if (state.progress >= 1) { if (state.progress >= 1) {
// ✅ Release waypoint once passed
releaseWaypoint(nextWaypointId);
state.pointIndex++; state.pointIndex++;
state.progress = 0; state.progress = 0;
// ✅ reached end of current path → switch to next path
if (state.pointIndex >= pathPoints.length - 1) { if (state.pointIndex >= pathPoints.length - 1) {
state.pathIndex++; state.pathIndex++;
state.pointIndex = 0; state.pointIndex = 0;
state.progress = 0;
// 🔥 refresh path data after advancing if (state.pathIndex >= route.length) {
if (state.pathIndex < route.length) { return;
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;
} }
currentPath = route[state.pathIndex];
pathPoints = currentPath.pathPoints;
} }
} }
@@ -286,151 +169,129 @@ export default function VehicleAnimator2({
direction direction
); );
mesh.quaternion.slerp(targetQuat, 0.1); 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) => { // const normalize = (v: any) => String(v ?? "").trim();
// if (!isPlaying) return;
// vehiclesData.forEach((vehicle) => { // // Find path
// console.log("vehiclesData: ", vehiclesData); // const path = paths.find(
// const { vehicleId, route } = vehicle; // (p: any) => normalize(p.pathId) === normalize(pathId)
// if (!route || route.length === 0) return; // );
// if (!path) {
//
// return false;
// }
// const mesh = scene.getObjectByProperty("uuid", vehicleId); // // If path already reserved → always reject
// if (!mesh) return; // if (!path.isAvailable) {
// console.log(
// const uuid = vehicleId; // `🚨 Path ${pathId} is already reserved by vehicle ${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
// ); // );
// return false;
// }
//
// const segmentVector = new Vector3().subVectors(nextPoint, currentPoint); // // Reserve the path for this vehicle
// const segmentLength = segmentVector.length(); // const updated = allPaths.map((p: any) =>
// const direction = segmentVector.clone().normalize(); // normalize(p.pathId) === normalize(pathId)
// ? { ...p, vehicleId, isAvailable: false }
// : p
// );
// console.log("speed: ", speed); // setAllPaths(updated);
// const moveDistance = speed * delta;
// state.progress += moveDistance / segmentLength;
// if (state.progress >= 1) { //
// state.pointIndex++; // return true;
// state.progress = 0; // };
// // ✅ reached end of current path → switch to next path const handlePathCheck = (pathId: string, vehicleId: string) => {
// if (state.pointIndex >= pathPoints.length - 1) { const normalize = (v: any) => String(v ?? "").trim();
// state.pathIndex++;
// state.pointIndex = 0;
// state.progress = 0;
// // 🔥 refresh path data after advancing // Find path
// if (state.pathIndex < route.length) { const path = useCreatedPaths
// currentPath = route[state.pathIndex]; .getState()
// pathPoints = currentPath.pathPoints; .allPaths.find((p: any) => normalize(p.pathId) === normalize(pathId));
// } 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;
// }
// }
// }
// // Interpolate position if (!path) {
// const newPos = new Vector3().lerpVectors( return false;
// new Vector3(...pathPoints[state.pointIndex].position), }
// new Vector3(...pathPoints[state.pointIndex + 1].position),
// state.progress
// );
// mesh.position.copy(newPos);
// // Smooth rotation // If path already reserved → reject always
// const forward = new Vector3(0, 0, 1);
// const targetQuat = new Quaternion().setFromUnitVectors( if (!path.isAvailable) {
// forward, // console.warn(
// direction // `🚨 Path ${pathId} is already reserved by vehicle ${vehicleId}`
// ); // );
// mesh.quaternion.slerp(targetQuat, 0.1); 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( // const manager = usePathManager(
// managerRef.current?.pathId, // managerRef.current?.pathId,
// managerRef.current?.vehicleId // managerRef.current?.vehicleId
// ); // );
// useEffect(() => {
// console.log("managerRef.current: ", managerRef.current);
// if (managerRef.current) {
// console.log("Path manager updated:", manager);
// }
// }, [manager, managerRef.current]);
return null; return null;
} }
// const handlePathCheck = (pathId: string, vehicleId: string) => {
// const allPaths = useCreatedPaths.getState().allPaths;
// // find the path were 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]);

View File

@@ -234,10 +234,6 @@ export default function VehicleInstance2({
const index = vehiclesDataRef.current.findIndex( const index = vehiclesDataRef.current.findIndex(
(v) => v.vehicleId === modelUuid (v) => v.vehicleId === modelUuid
); );
console.log(
"vehiclesDataRef.current: ",
vehiclesDataRef.current
);
if (index !== -1) { if (index !== -1) {
const updatedVehicles = [...vehiclesDataRef.current]; const updatedVehicles = [...vehiclesDataRef.current];
@@ -265,6 +261,7 @@ export default function VehicleInstance2({
useEffect(() => { useEffect(() => {
const canvasElement = gl.domElement; const canvasElement = gl.domElement;
canvasElement.addEventListener("contextmenu", handleContextMenu); canvasElement.addEventListener("contextmenu", handleContextMenu);
console.log("vehiclesDataRef.current: ", vehiclesDataRef.current);
return () => { return () => {
canvasElement.removeEventListener("contextmenu", handleContextMenu); canvasElement.removeEventListener("contextmenu", handleContextMenu);
}; };

View File

@@ -21,11 +21,7 @@ function VehicleInstances() {
selectedPointId: val.point.uuid, selectedPointId: val.point.uuid,
})); }));
setVehiclesData(updatedVehicles); setVehiclesData(updatedVehicles);
}, [vehicles]); }, []);
useEffect(() => {
console.log("vehiclesData", vehiclesData);
}, [vehiclesData]);
return ( return (
<> <>

View File

@@ -63,7 +63,6 @@ export default function PathCreator() {
const POINT_SNAP_THRESHOLD = 0.5; const POINT_SNAP_THRESHOLD = 0.5;
const CAN_POINT_SNAP = true; const CAN_POINT_SNAP = true;
useEffect(() => {}, [paths]);
const getAllOtherPathPoints = useCallback((): PointData[] => { const getAllOtherPathPoints = useCallback((): PointData[] => {
if (draftPoints.length === 0) return []; if (draftPoints.length === 0) return [];
@@ -145,7 +144,6 @@ export default function PathCreator() {
]; ];
}); });
useEffect(() => {}, [paths]);
const getPathPointById = (uuid: any) => { const getPathPointById = (uuid: any) => {
for (const path of paths) { for (const path of paths) {