added temp vehicle schema that holds the path details and vehicle details
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ const VehicleUI = () => {
|
|||||||
steeringAngle: steeringRotation[1],
|
steeringAngle: steeringRotation[1],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -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}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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]);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
// }[];
|
||||||
Reference in New Issue
Block a user