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;
};
const onMouseUp = () => {
const onMouseUp = (e : MouseEvent) => {
if (selectedEventSphere && !drag) {
raycaster.setFromCamera(pointer, camera);
const intersects = raycaster.intersectObjects(scene.children, true).filter((intersect) => intersect.object.name === "Event-Sphere");
if (intersects.length === 0) {
if (intersects.length === 0 && e.button === 0) {
clearSelectedEventSphere();
setTransformMode(null);
}

View File

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

View File

@@ -1,365 +1,409 @@
import { useEffect, useRef, useState } from 'react'
import { useFrame, useThree, ThreeEvent } from '@react-three/fiber';
import * as THREE from 'three';
import { Line } from '@react-three/drei';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
import { useSceneContext } from '../../../../scene/sceneContext';
import { useActiveTool, useSelectedPath } from '../../../../../store/builder/store';
import { useEffect, useRef, useState } from "react";
import { useFrame, useThree, ThreeEvent } from "@react-three/fiber";
import * as THREE from "three";
import { Line } from "@react-three/drei";
import {
useAnimationPlaySpeed,
usePauseButtonStore,
usePlayButtonStore,
useResetButtonStore,
} from "../../../../../store/usePlayButtonStore";
import { useSceneContext } from "../../../../scene/sceneContext";
import {
useActiveTool,
useSelectedPath,
} from "../../../../../store/builder/store";
interface VehicleAnimatorProps {
path: [number, number, number][];
handleCallBack: () => void;
reset: () => void;
startUnloadingProcess: () => void;
currentPhase: string;
agvUuid: string;
agvDetail: VehicleStatus;
path: [number, number, number][];
handleCallBack: () => void;
reset: () => void;
startUnloadingProcess: () => void;
currentPhase: string;
agvUuid: string;
agvDetail: VehicleStatus;
}
function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetail, reset, startUnloadingProcess }: Readonly<VehicleAnimatorProps>) {
const { vehicleStore } = useSceneContext();
const { getVehicleById } = vehicleStore();
const { isPaused } = usePauseButtonStore();
const { isPlaying } = usePlayButtonStore();
const { speed } = useAnimationPlaySpeed();
const { isReset, setReset } = useResetButtonStore();
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>("");
function VehicleAnimator({
path,
handleCallBack,
currentPhase,
agvUuid,
agvDetail,
reset,
startUnloadingProcess,
}: Readonly<VehicleAnimatorProps>) {
const { vehicleStore } = useSceneContext();
const { getVehicleById } = vehicleStore();
const { isPaused } = usePauseButtonStore();
const { isPlaying } = usePlayButtonStore();
const { speed } = useAnimationPlaySpeed();
const { isReset, setReset } = useResetButtonStore();
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(() => {
if (currentPhase === 'stationed-pickup' && path.length > 0 && selectedPath === "auto") {
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);
useEffect(() => {
completedRef.current = false;
}, [currentPath]);
useEffect(() => {
if (isReset || !isPlaying) {
reset();
setCurrentPath([]);
completedRef.current = false;
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(() => {
completedRef.current = false;
}, [currentPath]);
const isAligned = angle < 0.01;
useEffect(() => {
if (isReset || !isPlaying) {
reset();
setCurrentPath([]);
completedRef.current = false;
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]);
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}
/>
);
}
}
}, [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);
}
}
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 >
}
</>
);
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;
function DraggableSphere({
index,
position,
onMove,
isAnyDragging,
setIsAnyDragging,
index,
position,
onMove,
isAnyDragging,
setIsAnyDragging,
}: {
index: number;
position: THREE.Vector3;
onMove: (index: number, pos: THREE.Vector3) => void;
isAnyDragging: string;
setIsAnyDragging: (val: string) => void;
index: number;
position: THREE.Vector3;
onMove: (index: number, pos: THREE.Vector3) => void;
isAnyDragging: string;
setIsAnyDragging: (val: string) => void;
}) {
const meshRef = useRef<THREE.Mesh>(null);
const { gl, controls, raycaster } = useThree();
const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
const { activeTool } = useActiveTool();
const meshRef = useRef<THREE.Mesh>(null);
const { gl, controls, raycaster } = useThree();
const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
const { activeTool } = useActiveTool();
const onPointerDown = (e: ThreeEvent<PointerEvent>) => {
e.stopPropagation()
if (activeTool !== 'pen') return;
setIsAnyDragging("point");
gl.domElement.style.cursor = 'grabbing';
if (controls) (controls as any).enabled = false;
const onPointerDown = (e: ThreeEvent<PointerEvent>) => {
e.stopPropagation();
if (activeTool !== "pen") return;
setIsAnyDragging("point");
gl.domElement.style.cursor = "grabbing";
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>) => {
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])
return (
<mesh
ref={meshRef}
position={position}
onPointerDown={onPointerDown}
onPointerMove={onPointerMove}
onPointerUp={onPointerUp}
onPointerMissed={onPointerUp}
>
<sphereGeometry args={[0.2, 16, 16]} />
<meshStandardMaterial color="red" />
</mesh>
);
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,
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;
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 { 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 { 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;
setIsAnyDragging("line");
gl.domElement.style.cursor = 'grabbing';
if (controls) (controls as any).enabled = false;
const onPointerDown = () => {
if (activeTool !== "pen" || isAnyDragging) return;
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;
};
useEffect(() => {
gl.domElement.addEventListener("pointerup", onPointerUp);
return () => {
gl.domElement.removeEventListener("pointerup", onPointerUp);
};
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])
return (
<Line
points={[start, end]}
color="blue"
lineWidth={5}
onPointerDown={onPointerDown}
onPointerMove={onPointerMove}
onPointerUp={onPointerUp}
onPointerMissed={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 VehicleContentUi from "../../ui3d/VehicleContentUi";
import { useSceneContext } from "../../../scene/sceneContext";
import { useViewSceneStore } from "../../../../store/builder/store";
import PathCreator from "../pathCreator/pathCreator";
import VehicleInstance2 from "./instance/vehicleInstance2";
function VehicleInstances() {
const { vehicleStore } = useSceneContext();
const { vehicles } = vehicleStore();
const { viewSceneLabels } = useViewSceneStore();
const { vehicleStore } = useSceneContext();
const { vehicles } = vehicleStore();
const [vehiclesData, setVehiclesData] = useState<VehicleStructure[]>([]);
return (
<>
{vehicles.map((vehicle: VehicleStatus) => (
<React.Fragment key={vehicle.modelUuid}>
<VehicleInstance agvDetail={vehicle} />
{viewSceneLabels && <VehicleContentUi vehicle={vehicle} />}
</React.Fragment>
))}
</>
);
useEffect(() => {
const updatedVehicles = vehicles.map((val) => ({
vehicleId: val.modelUuid,
position: val.position,
rotation: val.rotation,
startPoint: null,
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;

View File

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

View File

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

View File

@@ -22,6 +22,8 @@ import {
} from "../../../../store/usePlayButtonStore";
import { useSceneContext } from "../../../scene/sceneContext";
import { usePathManager } from "./function/usePathManager";
import { useProductContext } from "../../products/productContext";
import { useSelectedEventSphere } from "../../../../store/simulation/useSimulationStore";
type PointData = {
pointId: string;
position: [number, number, number];
@@ -202,6 +204,12 @@ export default function PointHandler({
const [multiPaths, setMultiPaths] = useState<
{ 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 { toolMode } = useToolMode();
const { activeTool } = useActiveTool();
@@ -230,19 +238,7 @@ export default function PointHandler({
const [vehicleData, setVehicleData] = useState<VehicleDetails[]>([]);
const { paths, setPaths } = useCreatedPaths();
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(() => {
const findVehicle = assets
.filter((val) => val.eventData?.type === "Vehicle")
@@ -417,87 +413,174 @@ export default function PointHandler({
removePathByPoint(point.pointId);
return;
}
};
let selectedVehiclePaths: Array<
Array<{
vehicleId: string;
pathId: string;
isActive?: boolean;
isCurved?: boolean;
pathPoints: [PointData, PointData];
}>
> = [];
if (e.shiftKey) {
const pointIndex = points.findIndex((p) => p.pointId === point.pointId);
if (pointIndex === -1) {
return;
function assignPathToSelectedVehicle(
selectedVehicleId: string,
currentPath: PathData
) {
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 (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
// );
// if (result) {
// const edges = nodePathToEdges(result.pointIds, points, paths);
//
// setShortestPaths(edges);
// setShortestEdges(edges);
// } else {
// setShortestPaths([]);
// setShortestEdges([]);
// }
// if (prevPoint.pointId === newPoint.pointId) {
// return prev;
// }
// }, 0);
// if (result) {
// 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];
// }
// return [prev[0], pointIndex];
// }
// More than two points — reset
if (prev.length === 1) {
setTimeout(() => {
const prevPoint = points[prev[0]];
const newPoint = points[pointIndex];
if (prevPoint.pointId === newPoint.pointId) return;
const result = aStarShortestPath(
prevPoint.pointId,
newPoint.pointId,
points,
paths
// More than two points — reset
if (prev.length === 1) {
setTimeout(() => {
const prevPoint = points[prev[0]];
const newPoint = points[pointIndex];
console.log(
"selectedEventSphere?.userData.modelUuid: ",
selectedEventSphere?.userData.modelUuid
);
if (selectedEventSphere?.userData.modelUuid) {
const updatedVehicle = getVehicleById(
selectedEventSphere.userData.modelUuid
);
if (result) {
const edges = nodePathToEdges(result.pointIds, points, paths);
const startPoint = new Vector3(...prevPoint.position);
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
const newPathObj = {
id: pathIdRef.current++,
path: edges,
};
setShortestPaths(edges);
setShortestEdges(edges);
// Append it to the list of paths
setMultiPaths((prevPaths) => [...prevPaths, newPathObj]);
if (prevPoint.pointId === newPoint.pointId) return;
const result = aStarShortestPath(
prevPoint.pointId,
newPoint.pointId,
points,
paths
);
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
}, 0);
// Reset selection to allow new pair selection
}, 0);
return [prev[0], pointIndex];
}
return [prev[0], pointIndex];
}
setShortestPaths([]);
return [pointIndex];
});
}
setShortestPaths([]);
return [pointIndex];
});
// }
};
const handleDragStart = (point: PointData) => {
@@ -630,10 +713,8 @@ export default function PointHandler({
}
return null; // not found
}
//
//works
useFrame((_, delta) => {
//
if (!isPlaying || pathSegments.length < 2 || vehicleData.length === 0)
return;
@@ -651,7 +732,6 @@ export default function PointHandler({
const pathStart = pathSegments[0].position;
// Step 1: Move vehicle to the start of the path
if (!state.hasStarted) {
const distanceToStart = object.position.distanceTo(pathStart);
const step = speed * delta;
@@ -659,11 +739,10 @@ export default function PointHandler({
if (distanceToStart <= step) {
object.position.copy(pathStart);
object.quaternion.identity();
state.hasStarted = true; // start path animation next frame
state.hasStarted = true;
return;
}
// Move toward path start
const direction = pathStart.clone().sub(object.position);
const distance = direction.length();
@@ -671,21 +750,19 @@ export default function PointHandler({
direction.normalize();
object.position.add(direction.clone().multiplyScalar(step));
// ROTATE toward direction of movement
const forward = new Vector3(0, 0, 1); // <-- adjust this based on your model
const forward = new Vector3(0, 0, 1);
const targetQuat = new Quaternion().setFromUnitVectors(
forward,
direction
);
object.quaternion.slerp(targetQuat, 0.1); // smooth rotation
object.quaternion.slerp(targetQuat, 0.1);
} else {
// Snap to start segment when close enough
object.position.copy(pathStart);
object.quaternion.identity(); // reset orientation or leave as-is
object.quaternion.identity();
vehicleMovementState.current[uuid] = { index: 0, progress: 0 };
}
return; // still approaching start
return;
}
// Step 2: Follow the path
@@ -707,14 +784,12 @@ export default function PointHandler({
state.index++;
if (state.index >= pathSegments.length - 1) {
// Path complete
state.index = 0;
state.progress = 0;
state.hasStarted = false; // reset for next loop
object.position.copy(vehicleStartPos); // go back to start
state.hasStarted = false;
object.position.copy(vehicleStartPos);
object.quaternion.identity();
// Move to next vehicle
setActiveVehicleIndex((prevIndex) =>
prevIndex + 1 >= vehicleData.length ? 0 : prevIndex + 1
);
@@ -737,6 +812,101 @@ export default function PointHandler({
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) => {
// if (!isPlaying || pathSegments.length < 2 || vehicleData.length === 0)
// return;
@@ -792,12 +962,12 @@ export default function PointHandler({
// object.quaternion.slerp(targetQuat, 0.1);
// });
useEffect(() => {}, [multiPaths]);
const manager = usePathManager(managerData?.pathId, managerData?.vehicleId);
useEffect(() => {
// if (managerData) {
// console.log("manager: ", manager);
//
// }
}, [managerData]);
return (
@@ -817,6 +987,7 @@ export default function PointHandler({
onClick={(e) => {
handlePointClick(e, point);
}}
// onContextMenu={(e) => handleContextMenu(e, point)}
onPointerOver={(e) => {
if (!hoveredPoint && e.buttons === 0 && !e.ctrlKey) {
setHoveredPoint(point);

View File

@@ -664,8 +664,71 @@ interface allPaths {
type PathData = PathDataInterface[];
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 }),
allPaths: [],
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;
// }[];