added temp vehicle schema that holds the path details and vehicle details

This commit is contained in:
2025-08-28 17:58:38 +05:30
parent a0e5115c6c
commit 6182862296
9 changed files with 1017 additions and 463 deletions

View File

@@ -180,11 +180,11 @@ function PointsCreator() {
drag = false; drag = false;
}; };
const onMouseUp = () => { const onMouseUp = (e : MouseEvent) => {
if (selectedEventSphere && !drag) { if (selectedEventSphere && !drag) {
raycaster.setFromCamera(pointer, camera); raycaster.setFromCamera(pointer, camera);
const intersects = raycaster.intersectObjects(scene.children, true).filter((intersect) => intersect.object.name === "Event-Sphere"); const intersects = raycaster.intersectObjects(scene.children, true).filter((intersect) => intersect.object.name === "Event-Sphere");
if (intersects.length === 0) { if (intersects.length === 0 && e.button === 0) {
clearSelectedEventSphere(); clearSelectedEventSphere();
setTransformMode(null); setTransformMode(null);
} }

View File

@@ -199,6 +199,7 @@ const VehicleUI = () => {
steeringAngle: steeringRotation[1], steeringAngle: steeringRotation[1],
}, },
}, },
} }
); );

View File

@@ -1,365 +1,409 @@
import { useEffect, useRef, useState } from "react";
import { useEffect, useRef, useState } from 'react' import { useFrame, useThree, ThreeEvent } from "@react-three/fiber";
import { useFrame, useThree, ThreeEvent } from '@react-three/fiber'; import * as THREE from "three";
import * as THREE from 'three'; import { Line } from "@react-three/drei";
import { Line } from '@react-three/drei'; import {
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; useAnimationPlaySpeed,
import { useSceneContext } from '../../../../scene/sceneContext'; usePauseButtonStore,
import { useActiveTool, useSelectedPath } from '../../../../../store/builder/store'; usePlayButtonStore,
useResetButtonStore,
} from "../../../../../store/usePlayButtonStore";
import { useSceneContext } from "../../../../scene/sceneContext";
import {
useActiveTool,
useSelectedPath,
} from "../../../../../store/builder/store";
interface VehicleAnimatorProps { interface VehicleAnimatorProps {
path: [number, number, number][]; path: [number, number, number][];
handleCallBack: () => void; handleCallBack: () => void;
reset: () => void; reset: () => void;
startUnloadingProcess: () => void; startUnloadingProcess: () => void;
currentPhase: string; currentPhase: string;
agvUuid: string; agvUuid: string;
agvDetail: VehicleStatus; agvDetail: VehicleStatus;
} }
function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetail, reset, startUnloadingProcess }: Readonly<VehicleAnimatorProps>) { function VehicleAnimator({
const { vehicleStore } = useSceneContext(); path,
const { getVehicleById } = vehicleStore(); handleCallBack,
const { isPaused } = usePauseButtonStore(); currentPhase,
const { isPlaying } = usePlayButtonStore(); agvUuid,
const { speed } = useAnimationPlaySpeed(); agvDetail,
const { isReset, setReset } = useResetButtonStore(); reset,
const progressRef = useRef<number>(0); startUnloadingProcess,
const movingForward = useRef<boolean>(true); }: Readonly<VehicleAnimatorProps>) {
const completedRef = useRef<boolean>(false); const { vehicleStore } = useSceneContext();
const [objectRotation, setObjectRotation] = useState<{ x: number; y: number; z: number } | undefined>(agvDetail.point?.action?.pickUpPoint?.rotation || { x: 0, y: 0, z: 0 }) const { getVehicleById } = vehicleStore();
const [restRotation, setRestingRotation] = useState<boolean>(true); const { isPaused } = usePauseButtonStore();
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); const { isPlaying } = usePlayButtonStore();
const { scene, controls } = useThree(); const { speed } = useAnimationPlaySpeed();
const { selectedPath } = useSelectedPath(); const { isReset, setReset } = useResetButtonStore();
const [isAnyDragging, setIsAnyDragging] = useState<string>(""); const progressRef = useRef<number>(0);
const movingForward = useRef<boolean>(true);
const completedRef = useRef<boolean>(false);
const [objectRotation, setObjectRotation] = useState<
{ x: number; y: number; z: number } | undefined
>(agvDetail.point?.action?.pickUpPoint?.rotation || { x: 0, y: 0, z: 0 });
const [restRotation, setRestingRotation] = useState<boolean>(true);
const [currentPath, setCurrentPath] = useState<[number, number, number][]>(
[]
);
const { scene, controls } = useThree();
const { selectedPath } = useSelectedPath();
const [isAnyDragging, setIsAnyDragging] = useState<string>("");
useEffect(() => {
if (currentPhase === "stationed-pickup" && path.length > 0) {
setCurrentPath(path);
setObjectRotation(agvDetail.point.action?.pickUpPoint?.rotation);
} else if (currentPhase === "pickup-drop" && path.length > 0) {
setObjectRotation(agvDetail.point.action?.unLoadPoint?.rotation);
setCurrentPath(path);
} else if (currentPhase === "drop-pickup" && path.length > 0) {
setObjectRotation(agvDetail.point.action?.pickUpPoint?.rotation);
setCurrentPath(path);
}
}, [currentPhase, path, objectRotation, selectedPath]);
useEffect(() => { useEffect(() => {
if (currentPhase === 'stationed-pickup' && path.length > 0 && selectedPath === "auto") { completedRef.current = false;
setCurrentPath(path); }, [currentPath]);
setObjectRotation(agvDetail.point.action?.pickUpPoint?.rotation)
} else if (currentPhase === 'pickup-drop' && path.length > 0) { useEffect(() => {
setObjectRotation(agvDetail.point.action?.unLoadPoint?.rotation) if (isReset || !isPlaying) {
setCurrentPath(path); reset();
} else if (currentPhase === 'drop-pickup' && path.length > 0) { setCurrentPath([]);
setObjectRotation(agvDetail.point.action?.pickUpPoint?.rotation) completedRef.current = false;
setCurrentPath(path); movingForward.current = true;
progressRef.current = 0;
setReset(false);
setRestingRotation(true);
const object = scene.getObjectByProperty("uuid", agvUuid);
const vehicle = getVehicleById(agvDetail.modelUuid);
if (object && vehicle) {
object.position.set(
vehicle.position[0],
vehicle.position[1],
vehicle.position[2]
);
object.rotation.set(
vehicle.rotation[0],
vehicle.rotation[1],
vehicle.rotation[2]
);
}
}
}, [isReset, isPlaying]);
const lastTimeRef = useRef(performance.now());
useFrame(() => {
if (!isPlaying) return;
const now = performance.now();
const delta = (now - lastTimeRef.current) / 1000;
lastTimeRef.current = now;
const object = scene.getObjectByProperty("uuid", agvUuid);
if (!object || currentPath.length < 2) return;
if (isPaused) return;
let totalDistance = 0;
const distances = [];
let accumulatedDistance = 0;
let index = 0;
const rotationSpeed = 0.75;
for (let i = 0; i < currentPath.length - 1; i++) {
const start = new THREE.Vector3(...currentPath[i]);
const end = new THREE.Vector3(...currentPath[i + 1]);
const segmentDistance = start.distanceTo(end);
distances.push(segmentDistance);
totalDistance += segmentDistance;
}
while (
index < distances.length &&
progressRef.current > accumulatedDistance + distances[index]
) {
accumulatedDistance += distances[index];
index++;
}
if (index < distances.length) {
const start = new THREE.Vector3(...currentPath[index]);
const end = new THREE.Vector3(...currentPath[index + 1]);
const segmentDistance = distances[index];
const targetQuaternion = new THREE.Quaternion().setFromRotationMatrix(
new THREE.Matrix4().lookAt(start, end, new THREE.Vector3(0, 1, 0))
);
const y180 = new THREE.Quaternion().setFromAxisAngle(
new THREE.Vector3(0, 1, 0),
Math.PI
);
targetQuaternion.multiply(y180);
const angle = object.quaternion.angleTo(targetQuaternion);
if (angle < 0.01) {
object.quaternion.copy(targetQuaternion);
} else {
const step = rotationSpeed * delta * speed * agvDetail.speed;
const angle = object.quaternion.angleTo(targetQuaternion);
if (angle < step) {
object.quaternion.copy(targetQuaternion);
} else {
object.quaternion.rotateTowards(targetQuaternion, step);
} }
}, [currentPhase, path, objectRotation, selectedPath]); }
useEffect(() => { const isAligned = angle < 0.01;
completedRef.current = false;
}, [currentPath]);
useEffect(() => { if (isAligned) {
if (isReset || !isPlaying) { progressRef.current += delta * (speed * agvDetail.speed);
reset(); const t = (progressRef.current - accumulatedDistance) / segmentDistance;
setCurrentPath([]); const position = start.clone().lerp(end, t);
completedRef.current = false; object.position.copy(position);
movingForward.current = true; }
progressRef.current = 0; }
setReset(false);
setRestingRotation(true); if (progressRef.current >= totalDistance) {
const object = scene.getObjectByProperty('uuid', agvUuid); if (restRotation && objectRotation) {
const vehicle = getVehicleById(agvDetail.modelUuid); const targetEuler = new THREE.Euler(
if (object && vehicle) { 0,
object.position.set(vehicle.position[0], vehicle.position[1], vehicle.position[2]); objectRotation.y - agvDetail.point.action.steeringAngle,
object.rotation.set(vehicle.rotation[0], vehicle.rotation[1], vehicle.rotation[2]); 0
);
const baseQuaternion = new THREE.Quaternion().setFromEuler(targetEuler);
const y180 = new THREE.Quaternion().setFromAxisAngle(
new THREE.Vector3(0, 1, 0),
Math.PI
);
const targetQuaternion = baseQuaternion.multiply(y180);
const angle = object.quaternion.angleTo(targetQuaternion);
if (angle < 0.01) {
object.quaternion.copy(targetQuaternion);
setRestingRotation(false);
} else {
const step = rotationSpeed * delta * speed * agvDetail.speed;
const angle = object.quaternion.angleTo(targetQuaternion);
if (angle < step) {
object.quaternion.copy(targetQuaternion);
} else {
object.quaternion.rotateTowards(targetQuaternion, step);
}
}
return;
}
}
if (progressRef.current >= totalDistance) {
setRestingRotation(true);
progressRef.current = 0;
movingForward.current = !movingForward.current;
setCurrentPath([]);
handleCallBack();
if (currentPhase === "pickup-drop") {
requestAnimationFrame(startUnloadingProcess);
}
}
});
const updatePoint = (index: number, pos: THREE.Vector3) => {
const updated = [...currentPath];
updated[index] = pos.toArray() as [number, number, number];
setCurrentPath(updated);
};
return (
<>
{selectedPath === "auto" && (
<group visible={false}>
{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);
}}
isAnyDragging={isAnyDragging}
setIsAnyDragging={setIsAnyDragging}
/>
);
} }
} return null;
}, [isReset, isPlaying]) })}
{currentPath.length > 0 && (
const lastTimeRef = useRef(performance.now()); <group
onPointerMissed={() => {
useFrame(() => { if (controls) (controls as any).enabled = true;
if (!isPlaying) return }}
const now = performance.now(); >
const delta = (now - lastTimeRef.current) / 1000; {currentPath.map((pos, i) => (
lastTimeRef.current = now; <DraggableSphere
key={i}
const object = scene.getObjectByProperty('uuid', agvUuid); index={i}
if (!object || currentPath.length < 2) return; position={new THREE.Vector3(...pos)}
if (isPaused) return; onMove={updatePoint}
isAnyDragging={isAnyDragging}
let totalDistance = 0; setIsAnyDragging={setIsAnyDragging}
const distances = []; />
let accumulatedDistance = 0; ))}
let index = 0; </group>
const rotationSpeed = 0.75; )}
</group>
for (let i = 0; i < currentPath.length - 1; i++) { )}
const start = new THREE.Vector3(...currentPath[i]); </>
const end = new THREE.Vector3(...currentPath[i + 1]); );
const segmentDistance = start.distanceTo(end);
distances.push(segmentDistance);
totalDistance += segmentDistance;
}
while (index < distances.length && progressRef.current > accumulatedDistance + distances[index]) {
accumulatedDistance += distances[index];
index++;
}
if (index < distances.length) {
const start = new THREE.Vector3(...currentPath[index]);
const end = new THREE.Vector3(...currentPath[index + 1]);
const segmentDistance = distances[index];
const targetQuaternion = new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().lookAt(start, end, new THREE.Vector3(0, 1, 0)));
const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
targetQuaternion.multiply(y180);
const angle = object.quaternion.angleTo(targetQuaternion);
if (angle < 0.01) {
object.quaternion.copy(targetQuaternion);
} else {
const step = rotationSpeed * delta * speed * agvDetail.speed;
const angle = object.quaternion.angleTo(targetQuaternion);
if (angle < step) {
object.quaternion.copy(targetQuaternion);
} else {
object.quaternion.rotateTowards(targetQuaternion, step);
}
}
const isAligned = angle < 0.01;
if (isAligned) {
progressRef.current += delta * (speed * agvDetail.speed);
const t = (progressRef.current - accumulatedDistance) / segmentDistance;
const position = start.clone().lerp(end, t);
object.position.copy(position);
}
}
if (progressRef.current >= totalDistance) {
if (restRotation && objectRotation) {
const targetEuler = new THREE.Euler(0, objectRotation.y - agvDetail.point.action.steeringAngle, 0);
const baseQuaternion = new THREE.Quaternion().setFromEuler(targetEuler);
const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
const targetQuaternion = baseQuaternion.multiply(y180);
const angle = object.quaternion.angleTo(targetQuaternion);
if (angle < 0.01) {
object.quaternion.copy(targetQuaternion);
setRestingRotation(false);
} else {
const step = rotationSpeed * delta * speed * agvDetail.speed;
const angle = object.quaternion.angleTo(targetQuaternion);
if (angle < step) {
object.quaternion.copy(targetQuaternion);
} else {
object.quaternion.rotateTowards(targetQuaternion, step);
}
}
return;
}
}
if (progressRef.current >= totalDistance) {
setRestingRotation(true);
progressRef.current = 0;
movingForward.current = !movingForward.current;
setCurrentPath([]);
handleCallBack();
if (currentPhase === 'pickup-drop') {
requestAnimationFrame(startUnloadingProcess);
}
}
});
const updatePoint = (index: number, pos: THREE.Vector3) => {
const updated = [...currentPath];
updated[index] = pos.toArray() as [number, number, number];
setCurrentPath(updated);
};
return (
<>
{selectedPath === "auto" &&
<group visible={false}>
{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);
}}
isAnyDragging={isAnyDragging}
setIsAnyDragging={setIsAnyDragging}
/>
);
}
return null;
})}
{currentPath.length > 0 && (
<group onPointerMissed={() => { if (controls) (controls as any).enabled = true; }}>
{currentPath.map((pos, i) =>
(
<DraggableSphere
key={i}
index={i}
position={new THREE.Vector3(...pos)}
onMove={updatePoint}
isAnyDragging={isAnyDragging}
setIsAnyDragging={setIsAnyDragging}
/>)
)}
</group >
)}
</group >
}
</>
);
} }
export default VehicleAnimator; export default VehicleAnimator;
function DraggableSphere({ function DraggableSphere({
index, index,
position, position,
onMove, onMove,
isAnyDragging, isAnyDragging,
setIsAnyDragging, setIsAnyDragging,
}: { }: {
index: number; index: number;
position: THREE.Vector3; position: THREE.Vector3;
onMove: (index: number, pos: THREE.Vector3) => void; onMove: (index: number, pos: THREE.Vector3) => void;
isAnyDragging: string; isAnyDragging: string;
setIsAnyDragging: (val: string) => void; setIsAnyDragging: (val: string) => void;
}) { }) {
const meshRef = useRef<THREE.Mesh>(null); const meshRef = useRef<THREE.Mesh>(null);
const { gl, controls, raycaster } = useThree(); const { gl, controls, raycaster } = useThree();
const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
const { activeTool } = useActiveTool(); const { activeTool } = useActiveTool();
const onPointerDown = (e: ThreeEvent<PointerEvent>) => { const onPointerDown = (e: ThreeEvent<PointerEvent>) => {
e.stopPropagation() e.stopPropagation();
if (activeTool !== 'pen') return; if (activeTool !== "pen") return;
setIsAnyDragging("point"); setIsAnyDragging("point");
gl.domElement.style.cursor = 'grabbing'; gl.domElement.style.cursor = "grabbing";
if (controls) (controls as any).enabled = false; if (controls) (controls as any).enabled = false;
};
const onPointerMove = (e: ThreeEvent<PointerEvent>) => {
if (isAnyDragging !== "point" || 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;
setIsAnyDragging("");
gl.domElement.style.cursor = "default";
if (controls) (controls as any).enabled = true;
};
useEffect(() => {
gl.domElement.addEventListener("pointerup", onPointerUp);
return () => {
gl.domElement.removeEventListener("pointerup", onPointerUp);
}; };
}, [activeTool]);
const onPointerMove = (e: ThreeEvent<PointerEvent>) => { return (
if (isAnyDragging !== "point" || activeTool !== 'pen') return; <mesh
ref={meshRef}
const intersect = new THREE.Vector3(); position={position}
if (raycaster.ray.intersectPlane(plane, intersect)) { onPointerDown={onPointerDown}
meshRef.current!.position.copy(intersect); onPointerMove={onPointerMove}
onMove(index, intersect); onPointerUp={onPointerUp}
} onPointerMissed={onPointerUp}
}; >
<sphereGeometry args={[0.2, 16, 16]} />
const onPointerUp = () => { <meshStandardMaterial color="red" />
if (activeTool !== 'pen') return; </mesh>
setIsAnyDragging(""); );
gl.domElement.style.cursor = 'default';
if (controls) (controls as any).enabled = true;
};
useEffect(() => {
gl.domElement.addEventListener("pointerup", onPointerUp);
return (() => {
gl.domElement.removeEventListener("pointerup", onPointerUp);
})
}, [activeTool])
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({ function DraggableLineSegment({
index, index,
start, start,
end, end,
updatePoints, updatePoints,
isAnyDragging, isAnyDragging,
setIsAnyDragging, setIsAnyDragging,
}: { }: {
index: number; index: number;
start: THREE.Vector3; start: THREE.Vector3;
end: THREE.Vector3; end: THREE.Vector3;
updatePoints: (i0: number, p0: THREE.Vector3, i1: number, p1: THREE.Vector3) => void; updatePoints: (
isAnyDragging: string; i0: number,
setIsAnyDragging: (val: string) => void; p0: THREE.Vector3,
i1: number,
p1: THREE.Vector3
) => void;
isAnyDragging: string;
setIsAnyDragging: (val: string) => void;
}) { }) {
const { gl, raycaster, controls } = useThree(); const { gl, raycaster, controls } = useThree();
const { activeTool } = useActiveTool(); const { activeTool } = useActiveTool();
const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
const dragStart = useRef<THREE.Vector3 | null>(null); const dragStart = useRef<THREE.Vector3 | null>(null);
const onPointerDown = () => { const onPointerDown = () => {
if (activeTool !== 'pen' || isAnyDragging) return; if (activeTool !== "pen" || isAnyDragging) return;
setIsAnyDragging("line"); setIsAnyDragging("line");
gl.domElement.style.cursor = 'grabbing'; gl.domElement.style.cursor = "grabbing";
if (controls) (controls as any).enabled = false; 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;
};
useEffect(() => {
gl.domElement.addEventListener("pointerup", onPointerUp);
return () => {
gl.domElement.removeEventListener("pointerup", onPointerUp);
}; };
}, [activeTool]);
const onPointerMove = (e: ThreeEvent<PointerEvent>) => { return (
if (isAnyDragging !== "line" || activeTool !== 'pen') return; <Line
points={[start, end]}
const intersect = new THREE.Vector3(); color="blue"
if (raycaster.ray.intersectPlane(plane, intersect)) { lineWidth={5}
if (!dragStart.current) dragStart.current = intersect.clone(); onPointerDown={onPointerDown}
const offset = new THREE.Vector3().subVectors(intersect, dragStart.current); onPointerMove={onPointerMove}
const newStart = start.clone().add(offset); onPointerUp={onPointerUp}
const newEnd = end.clone().add(offset); onPointerMissed={onPointerUp}
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;
};
useEffect(() => {
gl.domElement.addEventListener("pointerup", onPointerUp);
return (() => {
gl.domElement.removeEventListener("pointerup", onPointerUp);
})
}, [activeTool])
return (
<Line
points={[start, end]}
color="blue"
lineWidth={5}
onPointerDown={onPointerDown}
onPointerMove={onPointerMove}
onPointerUp={onPointerUp}
onPointerMissed={onPointerUp}
/>
);
} }

View File

@@ -0,0 +1,253 @@
import React, { useEffect, useMemo, useRef, useState } from "react";
import { useCreatedPaths } from "../../../../../store/builder/store";
import { useThree } from "@react-three/fiber";
import { Vector3 } from "three";
import { useSceneContext } from "../../../../scene/sceneContext";
import { useProductContext } from "../../../products/productContext";
import { useSelectedEventSphere } from "../../../../../store/simulation/useSimulationStore";
function dist(a: PointData, b: PointData): number {
return Math.sqrt(
(a.position[0] - b.position[0]) ** 2 +
(a.position[1] - b.position[1]) ** 2 +
(a.position[2] - b.position[2]) ** 2
);
}
type SegmentPoint = {
position: Vector3;
originalPoint?: PointData;
pathId?: string;
startId?: string;
endId?: string;
};
/** --- A* Algorithm --- */
type AStarResult = {
pointIds: string[];
distance: number;
};
function aStarShortestPath(
startId: string,
goalId: string,
points: PointData[],
paths: PathData
): AStarResult | null {
const pointById = new Map(points.map((p) => [p.pointId, p]));
const start = pointById.get(startId);
const goal = pointById.get(goalId);
if (!start || !goal) return null;
const openSet = new Set<string>([startId]);
const cameFrom: Record<string, string | null> = {};
const gScore: Record<string, number> = {};
const fScore: Record<string, number> = {};
for (const p of points) {
cameFrom[p.pointId] = null;
gScore[p.pointId] = Infinity;
fScore[p.pointId] = Infinity;
}
gScore[startId] = 0;
fScore[startId] = dist(start, goal);
const neighborsOf = (id: string): { id: string; cost: number }[] => {
const me = pointById.get(id)!;
const out: { id: string; cost: number }[] = [];
for (const edge of paths) {
const [a, b] = edge.pathPoints;
if (a.pointId === id) out.push({ id: b.pointId, cost: dist(me, b) });
else if (b.pointId === id) out.push({ id: a.pointId, cost: dist(me, a) });
}
return out;
};
while (openSet.size > 0) {
let current: string = [...openSet].reduce((a, b) =>
fScore[a] < fScore[b] ? a : b
);
if (current === goalId) {
const ids: string[] = [];
let node: string | null = current;
while (node) {
ids.unshift(node);
node = cameFrom[node];
}
return { pointIds: ids, distance: gScore[goalId] };
}
openSet.delete(current);
for (const nb of neighborsOf(current)) {
const tentativeG = gScore[current] + nb.cost;
if (tentativeG < gScore[nb.id]) {
cameFrom[nb.id] = current;
gScore[nb.id] = tentativeG;
fScore[nb.id] = tentativeG + dist(pointById.get(nb.id)!, goal);
openSet.add(nb.id);
}
}
}
return null;
}
/** --- Convert node path to edges --- */
function nodePathToEdges(
pointIds: string[],
points: PointData[],
paths: PathData
): PathData {
const byId = new Map(points.map((p) => [p.pointId, p]));
const edges: PathData = [];
for (let i = 0; i < pointIds.length - 1; i++) {
const a = pointIds[i];
const b = pointIds[i + 1];
const edge = paths.find(
(p) =>
(p.pathPoints[0].pointId === a && p.pathPoints[1].pointId === b) ||
(p.pathPoints[0].pointId === b && p.pathPoints[1].pointId === a)
);
if (edge) {
const [p1, p2] = edge.pathPoints;
edges.push({
pathId: edge.pathId,
pathPoints:
p1.pointId === a
? ([p1, p2] as [PointData, PointData])
: ([p2, p1] as [PointData, PointData]),
});
} else {
const pa = byId.get(a)!;
const pb = byId.get(b)!;
edges.push({
pathId: `synthetic-${a}-${b}`,
pathPoints: [pa, pb],
});
}
}
return edges;
}
interface VehicleInstanceProps {
vehicleData: VehicleStructure;
vehiclesData: VehicleStructure[];
setVehiclesData: React.Dispatch<React.SetStateAction<VehicleStructure[]>>;
}
export default function VehicleInstance2({
vehicleData,
vehiclesData,
setVehiclesData,
}: VehicleInstanceProps) {
const { paths, setPaths } = useCreatedPaths();
const { vehicleStore, productStore } = useSceneContext();
const { vehicles, getVehicleById } = vehicleStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { updateEvent, updateAction } = productStore();
const { selectedEventSphere } = useSelectedEventSphere();
const { scene, gl, raycaster } = useThree();
const [selected, setSelected] = useState<any>([]);
const allPoints = useMemo(() => {
const points: PointData[] = [];
const seen = new Set<string>();
paths?.forEach((path: PathDataInterface) => {
path.pathPoints.forEach((p) => {
if (!seen.has(p.pointId)) {
seen.add(p.pointId);
points.push(p);
}
});
});
return points;
}, [paths]);
const vehiclesDataRef = useRef(vehiclesData);
const selectedEventSphereRef = useRef(selectedEventSphere);
useEffect(() => {
vehiclesDataRef.current = vehiclesData;
}, [vehiclesData]);
useEffect(() => {
selectedEventSphereRef.current = selectedEventSphere;
}, [selectedEventSphere]);
const handleContextMenu = (e: any) => {
const intersectObject = raycaster.intersectObjects(scene.children);
if (intersectObject.length > 0) {
const pathPoint = intersectObject[0].object;
if (pathPoint.name === "Path-Point") {
const point: any = pathPoint.userData;
const pointIndex = allPoints.findIndex(
(p) => p.pointId === point.pointId
);
if (pointIndex === -1) return;
setSelected((prev: any) => {
if (prev.length === 0) {
return [pointIndex];
}
if (prev.length === 1) {
const prevPoint = allPoints[prev[0]];
console.log("prevPoint: ", prevPoint);
const newPoint = allPoints[pointIndex];
console.log("newPoint: ", newPoint);
if (prevPoint.pointId === newPoint.pointId) return prev;
const result = aStarShortestPath(
prevPoint.pointId,
newPoint.pointId,
allPoints,
paths
);
if (result) {
const edges = nodePathToEdges(result.pointIds, allPoints, paths);
setTimeout(() => {
const modelUuid = selectedEventSphere?.userData?.modelUuid;
const index = vehiclesData.findIndex(
(v) => v.vehicleId === modelUuid
);
if (index !== -1) {
const updatedVehicles = [...vehiclesData];
updatedVehicles[index] = {
...updatedVehicles[index],
startPoint: prevPoint.position,
endPoint: newPoint.position,
route: edges,
};
setVehiclesData(updatedVehicles);
}
}, 0);
}
return [prev[0], pointIndex];
}
return [pointIndex];
});
}
}
};
useEffect(() => {
const canvasElement = gl.domElement;
canvasElement.addEventListener("contextmenu", handleContextMenu);
return () => {
canvasElement.removeEventListener("contextmenu", handleContextMenu);
};
}, [raycaster]);
return null;
}

View File

@@ -1,24 +1,49 @@
import React from "react"; import React, { useEffect, useState } from "react";
import VehicleInstance from "./instance/vehicleInstance"; import VehicleInstance from "./instance/vehicleInstance";
import VehicleContentUi from "../../ui3d/VehicleContentUi"; import VehicleContentUi from "../../ui3d/VehicleContentUi";
import { useSceneContext } from "../../../scene/sceneContext"; import { useSceneContext } from "../../../scene/sceneContext";
import { useViewSceneStore } from "../../../../store/builder/store"; import { useViewSceneStore } from "../../../../store/builder/store";
import PathCreator from "../pathCreator/pathCreator";
import VehicleInstance2 from "./instance/vehicleInstance2";
function VehicleInstances() { function VehicleInstances() {
const { vehicleStore } = useSceneContext(); const { vehicleStore } = useSceneContext();
const { vehicles } = vehicleStore(); const { vehicles } = vehicleStore();
const { viewSceneLabels } = useViewSceneStore(); const [vehiclesData, setVehiclesData] = useState<VehicleStructure[]>([]);
return ( useEffect(() => {
<> const updatedVehicles = vehicles.map((val) => ({
{vehicles.map((vehicle: VehicleStatus) => ( vehicleId: val.modelUuid,
<React.Fragment key={vehicle.modelUuid}> position: val.position,
<VehicleInstance agvDetail={vehicle} /> rotation: val.rotation,
{viewSceneLabels && <VehicleContentUi vehicle={vehicle} />} startPoint: null,
</React.Fragment> endPoint: null,
))} selectedPointId: val.point.uuid,
</> }));
);
console.log("updatedVehicles: ", updatedVehicles);
setVehiclesData(updatedVehicles);
}, [vehicles]);
return (
<>
{/* {vehicles.map((vehicle: VehicleStatus) => (
<React.Fragment key={vehicle.modelUuid}>
<VehicleInstance agvDetail={vehicle} />
{viewSceneLabels && <VehicleContentUi vehicle={vehicle} />}
</React.Fragment>
))} */}
{vehiclesData.map((vehicle: VehicleStructure) => (
<React.Fragment key={vehicle.vehicleId}>
<VehicleInstance2
vehicleData={vehicle}
vehiclesData={vehiclesData}
setVehiclesData={setVehiclesData}
/>
</React.Fragment>
))}
</>
);
} }
export default VehicleInstances; export default VehicleInstances;

View File

@@ -3,7 +3,7 @@ import { useCreatedPaths } from "../../../../../store/builder/store";
export const usePathManager = (pathId?: string, vehicleId?: string | null) => { export const usePathManager = (pathId?: string, vehicleId?: string | null) => {
const { paths, allPaths, setAllPaths } = useCreatedPaths(); const { paths, allPaths, setAllPaths } = useCreatedPaths();
console.log("allPaths: ", allPaths);
useEffect(() => { useEffect(() => {
if (!paths || paths.length === 0) return; if (!paths || paths.length === 0) return;

View File

@@ -35,11 +35,6 @@ interface PathDataInterface {
type PathData = PathDataInterface[]; type PathData = PathDataInterface[];
export default function PathCreator() { export default function PathCreator() {
// const [paths, setPaths] = useState<PathData>([]);
// const [paths, setPaths] = useState<PathData>(() => {
// const stored = localStorage.getItem("paths");
// return stored ? JSON.parse(stored) : [];
// });
const { paths, setPaths } = useCreatedPaths(); const { paths, setPaths } = useCreatedPaths();
const { activeTool } = useActiveTool(); const { activeTool } = useActiveTool();
const { toolMode } = useToolMode(); const { toolMode } = useToolMode();
@@ -62,9 +57,9 @@ export default function PathCreator() {
[] []
); );
const plane = useMemo(() => new Plane(new Vector3(0, 1, 0), 0), []); const plane = useMemo(() => new Plane(new Vector3(0, 1, 0), 0), []);
const { scene, raycaster, gl } = useThree(); const { scene, raycaster, gl } = useThree();
const POINT_SNAP_THRESHOLD = 0.5; const POINT_SNAP_THRESHOLD = 0.5;
const CAN_POINT_SNAP = true; const CAN_POINT_SNAP = true;
@@ -80,8 +75,10 @@ export default function PathCreator() {
) ?? [] ) ?? []
); );
}, [paths, draftPoints]); }, [paths, draftPoints]);
useEffect(() => { useEffect(() => {
const stored = localStorage.getItem("paths"); const stored = localStorage.getItem("paths");
setPaths(stored ? JSON.parse(stored) : []); setPaths(stored ? JSON.parse(stored) : []);
}, []); }, []);
@@ -344,7 +341,7 @@ export default function PathCreator() {
const allPoints = useMemo(() => { const allPoints = useMemo(() => {
const points: PointData[] = []; const points: PointData[] = [];
const seen = new Set<string>(); const seen = new Set<string>();
console.log("paths: ", paths);
paths?.forEach((path: PathDataInterface) => { paths?.forEach((path: PathDataInterface) => {
path.pathPoints.forEach((p) => { path.pathPoints.forEach((p) => {
if (!seen.has(p.pointId)) { if (!seen.has(p.pointId)) {
@@ -355,9 +352,9 @@ export default function PathCreator() {
}); });
return points; return points;
}, [paths]); }, [paths]);
useEffect(() => { useEffect(() => {
console.log("paths ", paths);
localStorage.setItem("paths", JSON.stringify(paths)); localStorage.setItem("paths", JSON.stringify(paths));
}, [paths]); }, [paths]);

View File

@@ -22,6 +22,8 @@ import {
} from "../../../../store/usePlayButtonStore"; } from "../../../../store/usePlayButtonStore";
import { useSceneContext } from "../../../scene/sceneContext"; import { useSceneContext } from "../../../scene/sceneContext";
import { usePathManager } from "./function/usePathManager"; import { usePathManager } from "./function/usePathManager";
import { useProductContext } from "../../products/productContext";
import { useSelectedEventSphere } from "../../../../store/simulation/useSimulationStore";
type PointData = { type PointData = {
pointId: string; pointId: string;
position: [number, number, number]; position: [number, number, number];
@@ -202,6 +204,12 @@ export default function PointHandler({
const [multiPaths, setMultiPaths] = useState< const [multiPaths, setMultiPaths] = useState<
{ id: number; path: PathData }[] { id: number; path: PathData }[]
>([]); >([]);
const { vehicleStore, productStore } = useSceneContext();
const { vehicles, getVehicleById } = vehicleStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { updateEvent, updateAction } = productStore();
const { selectedEventSphere } = useSelectedEventSphere();
const pathIdRef = useRef(1); // To ensure unique incremental IDs const pathIdRef = useRef(1); // To ensure unique incremental IDs
const { toolMode } = useToolMode(); const { toolMode } = useToolMode();
const { activeTool } = useActiveTool(); const { activeTool } = useActiveTool();
@@ -230,19 +238,7 @@ export default function PointHandler({
const [vehicleData, setVehicleData] = useState<VehicleDetails[]>([]); const [vehicleData, setVehicleData] = useState<VehicleDetails[]>([]);
const { paths, setPaths } = useCreatedPaths(); const { paths, setPaths } = useCreatedPaths();
const [managerData, setManagerData] = useState<Manager>(); const [managerData, setManagerData] = useState<Manager>();
// useEffect(() => {
// const findVehicle = assets
// .filter((val) => val.eventData?.type === "Vehicle")
// ?.map((val) => val.modelUuid);
//
// setVehicleUuids(findVehicle);
// vehicleMovementState.current = {};
// findVehicle.forEach((uuid) => {
// vehicleMovementState.current[uuid] = { index: 0, progress: 0 };
// });
// }, [assets]);
useEffect(() => { useEffect(() => {
const findVehicle = assets const findVehicle = assets
.filter((val) => val.eventData?.type === "Vehicle") .filter((val) => val.eventData?.type === "Vehicle")
@@ -417,87 +413,174 @@ export default function PointHandler({
removePathByPoint(point.pointId); removePathByPoint(point.pointId);
return; return;
} }
};
let selectedVehiclePaths: Array<
Array<{
vehicleId: string;
pathId: string;
isActive?: boolean;
isCurved?: boolean;
pathPoints: [PointData, PointData];
}>
> = [];
if (e.shiftKey) { function assignPathToSelectedVehicle(
const pointIndex = points.findIndex((p) => p.pointId === point.pointId); selectedVehicleId: string,
if (pointIndex === -1) { currentPath: PathData
return; ) {
const vehiclePathSegments = currentPath.map((path) => ({
vehicleId: selectedVehicleId,
...path,
}));
return selectedVehiclePaths.push(vehiclePathSegments);
}
const handleContextMenu = (e: any, point: PointData) => {
// if (e.shiftKey && e.button === 2) {
const pointIndex = points.findIndex((p) => p.pointId === point.pointId);
if (pointIndex === -1) {
return;
}
setSelected((prev) => {
if (prev.length === 0) {
return [pointIndex];
} }
// if (prev.length === 1) {
// setTimeout(() => {
//
// const prevPoint = points[prev[0]];
//
// const newPoint = points[pointIndex];
//
// const result = aStarShortestPath(
// prevPoint.pointId,
// newPoint.pointId,
// points,
// paths
// );
setSelected((prev) => { // if (result) {
if (prev.length === 0) { // const edges = nodePathToEdges(result.pointIds, points, paths);
return [pointIndex]; //
} // setShortestPaths(edges);
// if (prev.length === 1) { // setShortestEdges(edges);
// setTimeout(() => { // } else {
// // setShortestPaths([]);
// const prevPoint = points[prev[0]]; // setShortestEdges([]);
// // }
// const newPoint = points[pointIndex]; // if (prevPoint.pointId === newPoint.pointId) {
// // return prev;
// const result = aStarShortestPath( // }
// prevPoint.pointId, // }, 0);
// newPoint.pointId,
// points,
// paths
// );
// if (result) { // return [prev[0], pointIndex];
// const edges = nodePathToEdges(result.pointIds, points, paths); // }
//
// setShortestPaths(edges);
// setShortestEdges(edges);
// } else {
// setShortestPaths([]);
// setShortestEdges([]);
// }
// if (prevPoint.pointId === newPoint.pointId) {
// return prev;
// }
// }, 0);
// return [prev[0], pointIndex]; // More than two points — reset
// } if (prev.length === 1) {
setTimeout(() => {
// More than two points — reset const prevPoint = points[prev[0]];
if (prev.length === 1) { const newPoint = points[pointIndex];
setTimeout(() => { console.log(
const prevPoint = points[prev[0]]; "selectedEventSphere?.userData.modelUuid: ",
const newPoint = points[pointIndex]; selectedEventSphere?.userData.modelUuid
);
if (prevPoint.pointId === newPoint.pointId) return; if (selectedEventSphere?.userData.modelUuid) {
const updatedVehicle = getVehicleById(
const result = aStarShortestPath( selectedEventSphere.userData.modelUuid
prevPoint.pointId,
newPoint.pointId,
points,
paths
); );
if (result) { const startPoint = new Vector3(...prevPoint.position);
const edges = nodePathToEdges(result.pointIds, points, paths); const endPoint = new Vector3(...newPoint.position);
if (updatedVehicle && startPoint && endPoint) {
if (updatedVehicle.type === "vehicle") {
const event = updateAction(
selectedProduct.productUuid,
updatedVehicle.point?.action.actionUuid,
{
pickUpPoint: {
position: {
x: startPoint.x,
y: 0,
z: startPoint.z,
},
rotation: {
x:
updatedVehicle.point.action.pickUpPoint?.rotation.x ??
0,
y:
updatedVehicle.point.action.pickUpPoint?.rotation.y ??
0,
z:
updatedVehicle.point.action.pickUpPoint?.rotation.z ??
0,
},
},
unLoadPoint: {
position: {
x: endPoint.x,
y: endPoint.y,
z: endPoint.z,
},
rotation: {
x:
updatedVehicle.point.action.unLoadPoint?.rotation.x ??
0,
y:
updatedVehicle.point.action.unLoadPoint?.rotation.y ??
0,
z:
updatedVehicle.point.action.unLoadPoint?.rotation.z ??
0,
},
},
}
);
// Create a new path object if (prevPoint.pointId === newPoint.pointId) return;
const newPathObj = {
id: pathIdRef.current++, const result = aStarShortestPath(
path: edges, prevPoint.pointId,
}; newPoint.pointId,
setShortestPaths(edges); points,
setShortestEdges(edges); paths
// Append it to the list of paths );
setMultiPaths((prevPaths) => [...prevPaths, newPathObj]);
if (result) {
const edges = nodePathToEdges(result.pointIds, points, paths);
// Create a new path object/
const newPathObj = {
id: pathIdRef.current++,
path: edges,
};
const shortPath = assignPathToSelectedVehicle(
updatedVehicle?.modelUuid,
edges
);
console.log("shortPath: ", shortPath);
setShortestPaths(edges);
setShortestEdges(edges);
// Append it to the list of paths
setMultiPaths((prevPaths) => [...prevPaths, newPathObj]);
}
}
} }
}
// Reset selection to allow new pair selection // Reset selection to allow new pair selection
}, 0); }, 0);
return [prev[0], pointIndex]; return [prev[0], pointIndex];
} }
setShortestPaths([]); setShortestPaths([]);
return [pointIndex]; return [pointIndex];
}); });
} // }
}; };
const handleDragStart = (point: PointData) => { const handleDragStart = (point: PointData) => {
@@ -630,10 +713,8 @@ export default function PointHandler({
} }
return null; // not found return null; // not found
} }
//works
//
useFrame((_, delta) => { useFrame((_, delta) => {
//
if (!isPlaying || pathSegments.length < 2 || vehicleData.length === 0) if (!isPlaying || pathSegments.length < 2 || vehicleData.length === 0)
return; return;
@@ -651,7 +732,6 @@ export default function PointHandler({
const pathStart = pathSegments[0].position; const pathStart = pathSegments[0].position;
// Step 1: Move vehicle to the start of the path
if (!state.hasStarted) { if (!state.hasStarted) {
const distanceToStart = object.position.distanceTo(pathStart); const distanceToStart = object.position.distanceTo(pathStart);
const step = speed * delta; const step = speed * delta;
@@ -659,11 +739,10 @@ export default function PointHandler({
if (distanceToStart <= step) { if (distanceToStart <= step) {
object.position.copy(pathStart); object.position.copy(pathStart);
object.quaternion.identity(); object.quaternion.identity();
state.hasStarted = true; // start path animation next frame state.hasStarted = true;
return; return;
} }
// Move toward path start
const direction = pathStart.clone().sub(object.position); const direction = pathStart.clone().sub(object.position);
const distance = direction.length(); const distance = direction.length();
@@ -671,21 +750,19 @@ export default function PointHandler({
direction.normalize(); direction.normalize();
object.position.add(direction.clone().multiplyScalar(step)); object.position.add(direction.clone().multiplyScalar(step));
// ROTATE toward direction of movement const forward = new Vector3(0, 0, 1);
const forward = new Vector3(0, 0, 1); // <-- adjust this based on your model
const targetQuat = new Quaternion().setFromUnitVectors( const targetQuat = new Quaternion().setFromUnitVectors(
forward, forward,
direction direction
); );
object.quaternion.slerp(targetQuat, 0.1); // smooth rotation object.quaternion.slerp(targetQuat, 0.1);
} else { } else {
// Snap to start segment when close enough
object.position.copy(pathStart); object.position.copy(pathStart);
object.quaternion.identity(); // reset orientation or leave as-is object.quaternion.identity();
vehicleMovementState.current[uuid] = { index: 0, progress: 0 }; vehicleMovementState.current[uuid] = { index: 0, progress: 0 };
} }
return; // still approaching start return;
} }
// Step 2: Follow the path // Step 2: Follow the path
@@ -707,14 +784,12 @@ export default function PointHandler({
state.index++; state.index++;
if (state.index >= pathSegments.length - 1) { if (state.index >= pathSegments.length - 1) {
// Path complete
state.index = 0; state.index = 0;
state.progress = 0; state.progress = 0;
state.hasStarted = false; // reset for next loop state.hasStarted = false;
object.position.copy(vehicleStartPos); // go back to start object.position.copy(vehicleStartPos);
object.quaternion.identity(); object.quaternion.identity();
// Move to next vehicle
setActiveVehicleIndex((prevIndex) => setActiveVehicleIndex((prevIndex) =>
prevIndex + 1 >= vehicleData.length ? 0 : prevIndex + 1 prevIndex + 1 >= vehicleData.length ? 0 : prevIndex + 1
); );
@@ -737,6 +812,101 @@ export default function PointHandler({
object.quaternion.slerp(targetQuat, 0.1); object.quaternion.slerp(targetQuat, 0.1);
}); });
// useFrame((_, delta) => {
// if (!isPlaying || pathSegments.length < 2 || vehicleData.length === 0)
// return;
// const vehicleStartPos = new Vector3(...agvDetail.position);
// const object = scene.getObjectByProperty("uuid", agvDetail.modelUuid);
// if (!object) return;
// const state = vehicleMovementState.current[agvDetail.modelUuid];
// if (!state) return;
// const moveSpeed = speed * delta;
// const pathStart = pathSegments[0].position;
// // ➤ Step 1: Move the object to the starting point of the path
// if (!state.hasStarted) {
// const distanceToStart = object.position.distanceTo(pathStart);
// if (distanceToStart <= moveSpeed) {
// object.position.copy(pathStart);
// object.quaternion.identity();
// state.hasStarted = true;
// state.index = 0;
// state.progress = 0;
// return;
// }
// const direction = pathStart.clone().sub(object.position).normalize();
// object.position.add(direction.clone().multiplyScalar(moveSpeed));
// const forward = new Vector3(0, 0, 1);
// const targetQuat = new Quaternion().setFromUnitVectors(
// forward,
// direction
// );
// object.quaternion.slerp(targetQuat, 0.1);
// return;
// }
// // ➤ Step 2: Traverse path segments
// const startSeg = pathSegments[state.index];
// const endSeg = pathSegments[state.index + 1];
// if (!startSeg || !endSeg) return;
// const currentPathId = getPathIdByPoints(
// startSeg.startId || startSeg.endId,
// endSeg.endId || endSeg.startId,
// shortestPaths
// );
// setManagerData({ pathId: currentPathId, vehicleId: agvDetail.modelUuid });
// const segmentDistance = startSeg.position.distanceTo(endSeg.position);
// state.progress += moveSpeed / segmentDistance;
// // ➤ Step 3: If we've finished this segment, move to the next
// while (state.progress >= 1) {
// state.progress -= 1;
// state.index++;
// if (state.index >= pathSegments.length - 1) {
// // Path completed
// state.index = 0;
// state.progress = 0;
// state.hasStarted = false;
// object.position.copy(vehicleStartPos);
// object.quaternion.identity();
// setActiveVehicleIndex((prevIndex) =>
// prevIndex + 1 >= vehicleData.length ? 0 : prevIndex + 1
// );
// return;
// }
// }
// // ➤ Step 4: Interpolate position and rotate
// const newPos = startSeg.position
// .clone()
// .lerp(endSeg.position, state.progress);
// object.position.copy(newPos);
// const direction = endSeg.position
// .clone()
// .sub(startSeg.position)
// .normalize();
// const forward = new Vector3(0, 0, 1);
// const targetQuat = new Quaternion().setFromUnitVectors(forward, direction);
// object.quaternion.slerp(targetQuat, 0.1);
// });
// useFrame((_, delta) => { // useFrame((_, delta) => {
// if (!isPlaying || pathSegments.length < 2 || vehicleData.length === 0) // if (!isPlaying || pathSegments.length < 2 || vehicleData.length === 0)
// return; // return;
@@ -792,12 +962,12 @@ export default function PointHandler({
// object.quaternion.slerp(targetQuat, 0.1); // object.quaternion.slerp(targetQuat, 0.1);
// }); // });
useEffect(() => {}, [multiPaths]);
const manager = usePathManager(managerData?.pathId, managerData?.vehicleId); const manager = usePathManager(managerData?.pathId, managerData?.vehicleId);
useEffect(() => { useEffect(() => {
// if (managerData) { // if (managerData) {
// console.log("manager: ", manager); //
// } // }
}, [managerData]); }, [managerData]);
return ( return (
@@ -817,6 +987,7 @@ export default function PointHandler({
onClick={(e) => { onClick={(e) => {
handlePointClick(e, point); handlePointClick(e, point);
}} }}
// onContextMenu={(e) => handleContextMenu(e, point)}
onPointerOver={(e) => { onPointerOver={(e) => {
if (!hoveredPoint && e.buttons === 0 && !e.ctrlKey) { if (!hoveredPoint && e.buttons === 0 && !e.ctrlKey) {
setHoveredPoint(point); setHoveredPoint(point);

View File

@@ -664,8 +664,71 @@ interface allPaths {
type PathData = PathDataInterface[]; type PathData = PathDataInterface[];
export const useCreatedPaths = create<any>((set: any) => ({ export const useCreatedPaths = create<any>((set: any) => ({
paths: [], paths: [
{
pathId: "276724c5-05a3-4b5e-a127-a60b3533ccce",
pathPoints: [
{
pointId: "19c3f429-f214-4f87-8906-7eaaedd925da",
position: [2.33155763270131, 0, -20.538859668988927],
},
{
pointId: "ea73c7c8-0e26-4aae-9ed8-2349ff2d6718",
position: [17.13371069714903, 0, -22.156135485080462],
},
],
},
{
pathId: "2736b20b-a433-443c-a5c9-5ba4348ac682",
pathPoints: [
{
pointId: "ea73c7c8-0e26-4aae-9ed8-2349ff2d6718",
position: [17.13371069714903, 0, -22.156135485080462],
},
{
pointId: "2212bb52-c63c-4289-8b50-5ffd229d13e5",
position: [16.29236816120279, 0, -10.819973445497789],
},
],
},
{
pathId: "3144a2df-7aad-483d-bbc7-de7f7b5b3bfc",
pathPoints: [
{
pointId: "2212bb52-c63c-4289-8b50-5ffd229d13e5",
position: [16.29236816120279, 0, -10.819973445497789],
},
{
pointId: "adfd05a7-4e16-403f-81d0-ce99f2e34f5f",
position: [4.677047323894161, 0, -8.279486846767094],
},
],
},
{
pathId: "e0a1b5da-27c2-44a0-81db-759b5a5eb416",
pathPoints: [
{
pointId: "adfd05a7-4e16-403f-81d0-ce99f2e34f5f",
position: [4.677047323894161, 0, -8.279486846767094],
},
{
pointId: "19c3f429-f214-4f87-8906-7eaaedd925da",
position: [2.33155763270131, 0, -20.538859668988927],
},
],
},
],
setPaths: (x: PathData) => set({ paths: x }), setPaths: (x: PathData) => set({ paths: x }),
allPaths: [], allPaths: [],
setAllPaths: (x: allPaths) => set({ allPaths: x }), setAllPaths: (x: allPaths) => set({ allPaths: x }),
})); }));
// route?: {
// pathId: string;
// pathPoints: {
// pointId: string;
// position: [number, number, number];
// isCurved?: boolean;
// handleA?: [number, number, number] | null;
// handleB: [number, number, number] | null;
// }[];