Merge remote-tracking branch 'origin/feature/agv-edit' into main-demo
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
useToolMode,
|
||||
useRenderDistance,
|
||||
useLimitDistance,
|
||||
useLoadingProgress,
|
||||
} from "../../store/builder/store";
|
||||
|
||||
////////// 3D Function Imports //////////
|
||||
@@ -56,6 +57,7 @@ export default function Builder() {
|
||||
const { projectId } = useParams();
|
||||
const { setHoveredPoint, setHoveredLine } = useBuilderStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { loadingProgress } = useLoadingProgress();
|
||||
|
||||
useEffect(() => {
|
||||
if (!toggleView) {
|
||||
@@ -115,8 +117,7 @@ export default function Builder() {
|
||||
|
||||
<CalculateAreaGroup />
|
||||
|
||||
<NavMesh />
|
||||
|
||||
{loadingProgress == 0 && <NavMesh />}
|
||||
<DxfFile />
|
||||
|
||||
<LayoutImage />
|
||||
|
||||
@@ -20,7 +20,7 @@ function ZoneCreator() {
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const { socket } = useSocketStore();
|
||||
const { zoneStore } = useSceneContext();
|
||||
const { addZone, getZonePointById, getZoneByPoints } = zoneStore();
|
||||
const { zones, addZone, getZonePointById, getZoneByPoints } = zoneStore();
|
||||
const drag = useRef(false);
|
||||
const isLeftMouseDown = useRef(false);
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
@@ -32,6 +32,7 @@ function ZoneCreator() {
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
const { zoneColor, zoneHeight, snappedPosition, snappedPoint, setSnappedPoint, setSnappedPosition } = useBuilderStore();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
@@ -92,7 +93,7 @@ function ZoneCreator() {
|
||||
if (tempPoints.length > 2 && isCreating && snappedPoint && snappedPoint.pointUuid === tempPoints[0].pointUuid) {
|
||||
const zone: Zone = {
|
||||
zoneUuid: THREE.MathUtils.generateUUID(),
|
||||
zoneName: "Zone",
|
||||
zoneName: `Zone `,
|
||||
points: tempPoints,
|
||||
zoneColor,
|
||||
zoneHeight,
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { Canvas, useThree, useFrame, ThreeEvent } from '@react-three/fiber';
|
||||
import { Line, OrbitControls } from '@react-three/drei';
|
||||
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||
import { useActiveTool, useSelectedPath } from '../../../../../store/builder/store';
|
||||
|
||||
interface InteractivePointsProps {
|
||||
agvUuid: string;
|
||||
}
|
||||
|
||||
|
||||
export default function InteractivePoints({ agvUuid }: InteractivePointsProps) {
|
||||
const { gl, scene, raycaster } = useThree();
|
||||
const [points, setPoints] = useState<[number, number, number][]>([]);
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { speed } = useAnimationPlaySpeed();
|
||||
const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)); // XZ plane
|
||||
const progressRef = useRef<number>(0);
|
||||
const { selectedPath } = useSelectedPath();
|
||||
const lastTimeRef = useRef(performance.now());
|
||||
const [isAnyDragging, setIsAnyDragging] = useState<string>("");
|
||||
const { activeTool } = useActiveTool();
|
||||
const hasClicked = useRef(false);
|
||||
|
||||
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 || points.length < 2) return;
|
||||
if (isPaused) return;
|
||||
|
||||
let totalDistance = 0;
|
||||
const distances = [];
|
||||
let accumulatedDistance = 0;
|
||||
let index = 0;
|
||||
const rotationSpeed = 1;
|
||||
|
||||
for (let i = 0; i < points.length - 1; i++) {
|
||||
const start = new THREE.Vector3(...points[i]);
|
||||
const end = new THREE.Vector3(...points[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(...points[index]);
|
||||
const end = new THREE.Vector3(...points[index + 1]);
|
||||
const segmentDistance = distances[index];
|
||||
|
||||
const currentDirection = new THREE.Vector3().subVectors(end, start).normalize();
|
||||
const targetAngle = Math.atan2(currentDirection.x, currentDirection.z);
|
||||
const currentAngle = object.rotation.y;
|
||||
|
||||
let angleDifference = targetAngle - currentAngle;
|
||||
if (angleDifference > Math.PI) angleDifference -= 2 * Math.PI;
|
||||
if (angleDifference < -Math.PI) angleDifference += 2 * Math.PI;
|
||||
|
||||
const maxRotationStep = (rotationSpeed * speed * 2) * delta;
|
||||
object.rotation.y += Math.sign(angleDifference) * Math.min(Math.abs(angleDifference), maxRotationStep);
|
||||
const isAligned = Math.abs(angleDifference) < 0.01;
|
||||
|
||||
if (isAligned) {
|
||||
progressRef.current += delta * (speed * 2);
|
||||
const t = (progressRef.current - accumulatedDistance) / segmentDistance;
|
||||
const position = start.clone().lerp(end, t);
|
||||
object.position.copy(position);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const downPosition = useRef<{ x: number; y: number } | null>(null);
|
||||
|
||||
const handleMouseDown = useCallback((e: MouseEvent) => {
|
||||
hasClicked.current = false;
|
||||
downPosition.current = { x: e.clientX, y: e.clientY };
|
||||
}, []);
|
||||
|
||||
const handleClick = useCallback((e: MouseEvent) => {
|
||||
if (hasClicked.current) return;
|
||||
hasClicked.current = true;
|
||||
|
||||
if (
|
||||
!downPosition.current ||
|
||||
Math.abs(downPosition.current.x - e.clientX) > 2 ||
|
||||
Math.abs(downPosition.current.y - e.clientY) > 2
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const intersection = new THREE.Vector3();
|
||||
if (raycaster.ray.intersectPlane(plane.current, intersection) && activeTool !== "pen") {
|
||||
const pointArray = intersection.toArray() as [number, number, number];
|
||||
|
||||
// ✅ Check if this point already exists
|
||||
const alreadyExists = points.some((p) =>
|
||||
Math.abs(p[0] - pointArray[0]) < 0.01 &&
|
||||
Math.abs(p[1] - pointArray[1]) < 0.01 &&
|
||||
Math.abs(p[2] - pointArray[2]) < 0.01
|
||||
);
|
||||
|
||||
if (!alreadyExists) {
|
||||
console.log("pointArray: ", pointArray);
|
||||
setPoints((prev) => [...prev, pointArray]);
|
||||
console.log("points created");
|
||||
}
|
||||
}
|
||||
}, [activeTool, raycaster, points]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying) return;
|
||||
const domElement = gl.domElement;
|
||||
|
||||
domElement.addEventListener('mousedown', handleMouseDown);
|
||||
domElement.addEventListener('mouseup', handleClick);
|
||||
|
||||
|
||||
return () => {
|
||||
domElement.removeEventListener('mousedown', handleMouseDown);
|
||||
domElement.removeEventListener('mouseup', handleClick);
|
||||
;
|
||||
};
|
||||
}, [isPlaying, handleClick, handleMouseDown]);
|
||||
|
||||
|
||||
const updatePoint = (index: number, pos: THREE.Vector3) => {
|
||||
const updated = [...points];
|
||||
updated[index] = pos.toArray() as [number, number, number];
|
||||
setPoints(updated);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{selectedPath === "manual" &&
|
||||
<group>
|
||||
{points.length > 0 && (
|
||||
<group >
|
||||
{points.map((pos, i) =>
|
||||
(<React.Fragment key={i}>
|
||||
<DraggableSphere
|
||||
key={i}
|
||||
index={i}
|
||||
position={new THREE.Vector3(...pos)}
|
||||
onMove={updatePoint}
|
||||
isAnyDragging={isAnyDragging}
|
||||
setIsAnyDragging={setIsAnyDragging}
|
||||
/>
|
||||
|
||||
</React.Fragment>)
|
||||
|
||||
|
||||
)}
|
||||
</group >
|
||||
)
|
||||
}
|
||||
{points && (
|
||||
points.map((pos, i) => {
|
||||
if (i < points.length - 1) {
|
||||
return (
|
||||
<DraggableLineSegment
|
||||
key={i}
|
||||
index={i}
|
||||
start={new THREE.Vector3(...points[i])}
|
||||
end={new THREE.Vector3(...points[i + 1])}
|
||||
updatePoints={(i0, p0, i1, p1) => {
|
||||
const updated = [...points];
|
||||
updated[i0] = p0.toArray() as [number, number, number];
|
||||
updated[i1] = p1.toArray() as [number, number, number];
|
||||
setPoints(updated);
|
||||
}}
|
||||
isAnyDragging={isAnyDragging}
|
||||
setIsAnyDragging={setIsAnyDragging}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})
|
||||
)}
|
||||
</group>
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function DraggableSphere({
|
||||
index,
|
||||
position,
|
||||
onMove,
|
||||
isAnyDragging,
|
||||
setIsAnyDragging,
|
||||
}: {
|
||||
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 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])
|
||||
|
||||
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: 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 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);
|
||||
})
|
||||
}, [activeTool])
|
||||
|
||||
return (
|
||||
<Line
|
||||
points={[start, end]}
|
||||
color="blue"
|
||||
lineWidth={5}
|
||||
onPointerDown={onPointerDown}
|
||||
onPointerMove={onPointerMove}
|
||||
onPointerUp={onPointerUp}
|
||||
onPointerMissed={onPointerUp}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useFrame, useThree } from '@react-three/fiber';
|
||||
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useFrame, useThree, ThreeEvent } from '@react-three/fiber';
|
||||
import * as THREE from 'three';
|
||||
import { Line } from '@react-three/drei';
|
||||
import { Line, TransformControls } 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][];
|
||||
@@ -28,10 +31,13 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
|
||||
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 } = useThree();
|
||||
const { scene, controls } = useThree();
|
||||
const { selectedPath } = useSelectedPath();
|
||||
const [isAnyDragging, setIsAnyDragging] = useState<string>("");
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (currentPhase === 'stationed-pickup' && path.length > 0) {
|
||||
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) {
|
||||
@@ -41,7 +47,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
|
||||
setObjectRotation(agvDetail.point.action?.pickUpPoint?.rotation)
|
||||
setCurrentPath(path);
|
||||
}
|
||||
}, [currentPhase, path, objectRotation]);
|
||||
}, [currentPhase, path, objectRotation, selectedPath]);
|
||||
|
||||
useEffect(() => {
|
||||
completedRef.current = false;
|
||||
@@ -68,6 +74,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
|
||||
const lastTimeRef = useRef(performance.now());
|
||||
|
||||
useFrame(() => {
|
||||
if (!isPlaying) return
|
||||
const now = performance.now();
|
||||
const delta = (now - lastTimeRef.current) / 1000;
|
||||
lastTimeRef.current = now;
|
||||
@@ -146,26 +153,198 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
|
||||
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 (
|
||||
<>
|
||||
{currentPath.length > 0 && (
|
||||
// helper
|
||||
<group visible={false}>
|
||||
<Line points={currentPath} color="blue" lineWidth={3} />
|
||||
{currentPath.map((point, index) => (
|
||||
<mesh key={index} position={point}>
|
||||
<sphereGeometry args={[0.1, 16, 16]} />
|
||||
<meshStandardMaterial color="red" />
|
||||
</mesh>
|
||||
))}
|
||||
</group>
|
||||
)}
|
||||
{selectedPath === "auto" && <group>
|
||||
{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({
|
||||
index,
|
||||
position,
|
||||
onMove,
|
||||
isAnyDragging,
|
||||
setIsAnyDragging,
|
||||
}: {
|
||||
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 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])
|
||||
|
||||
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: 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 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>) => {
|
||||
console.log('isAnyDragging: ', isAnyDragging);
|
||||
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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { NavMeshQuery } from '@recast-navigation/core';
|
||||
import { useNavMesh } from '../../../../../store/builder/store';
|
||||
import { useNavMesh, useSelectedPath } from '../../../../../store/builder/store';
|
||||
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
|
||||
import { useSceneContext } from '../../../../scene/sceneContext';
|
||||
import { useProductContext } from '../../../products/productContext';
|
||||
import InteractivePoints from '../animator/interactivePoint';
|
||||
|
||||
import MaterialAnimator from '../animator/materialAnimator';
|
||||
import VehicleAnimator from '../animator/vehicleAnimator';
|
||||
@@ -24,7 +25,6 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { vehicles, setVehicleActive, setVehicleState, setVehiclePicking, clearCurrentMaterials, setVehicleLoad, decrementVehicleLoad, removeLastMaterial, getLastMaterial, incrementIdleTime, incrementActiveTime, resetTime } = vehicleStore();
|
||||
|
||||
const [currentPhase, setCurrentPhase] = useState<string>('stationed');
|
||||
const [path, setPath] = useState<[number, number, number][]>([]);
|
||||
const pauseTimeRef = useRef<number | null>(null);
|
||||
@@ -38,6 +38,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const previousTimeRef = useRef<number | null>(null);
|
||||
const animationFrameIdRef = useRef<number | null>(null);
|
||||
const { selectedPath, setSelectedPath } = useSelectedPath();
|
||||
|
||||
useEffect(() => {
|
||||
isPausedRef.current = isPaused;
|
||||
@@ -57,12 +58,14 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
||||
Math.round(segmentPath[segmentPath.length - 1].x) == Math.round(end.x) &&
|
||||
Math.round(segmentPath[segmentPath.length - 1].z) == Math.round(end.z)
|
||||
) {
|
||||
console.log('segmentPath: ', segmentPath);
|
||||
return segmentPath?.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
|
||||
} else {
|
||||
console.log("There is no path here...Choose valid path")
|
||||
const { path: segmentPaths } = navMeshQuery.computePath(start, start);
|
||||
return segmentPaths.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
|
||||
}
|
||||
|
||||
} catch {
|
||||
console.error("Failed to compute path");
|
||||
return [];
|
||||
@@ -97,7 +100,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying) {
|
||||
if (isPlaying || selectedPath === "auto") {
|
||||
if (!agvDetail.point.action.unLoadPoint || !agvDetail.point.action.pickUpPoint) return;
|
||||
|
||||
if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'stationed') {
|
||||
@@ -105,10 +108,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
||||
new THREE.Vector3(agvDetail?.position[0], agvDetail?.position[1], agvDetail?.position[2]),
|
||||
agvDetail?.point?.action?.pickUpPoint?.position
|
||||
);
|
||||
// const toPickupPath = computePath(
|
||||
// new THREE.Vector3(agvDetail?.position[0], agvDetail?.position[1], agvDetail?.position[2]),
|
||||
// new THREE.Vector3(agvDetail?.position[0], agvDetail?.position[1], agvDetail?.position[2])
|
||||
// );
|
||||
|
||||
setPath(toPickupPath);
|
||||
setCurrentPhase('stationed-pickup');
|
||||
setVehicleState(agvDetail.modelUuid, 'running');
|
||||
@@ -149,8 +149,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
||||
else {
|
||||
reset()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [vehicles, currentPhase, path, isPlaying]);
|
||||
}, [vehicles, currentPhase, path, isPlaying, selectedPath]);
|
||||
|
||||
function animate(currentTime: number) {
|
||||
if (previousTimeRef.current === null) {
|
||||
@@ -600,6 +599,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
||||
reset={reset}
|
||||
startUnloadingProcess={startUnloadingProcess}
|
||||
/>
|
||||
{selectedPath === "manual" && (<InteractivePoints agvUuid={agvDetail?.modelUuid} />)}
|
||||
<MaterialAnimator agvDetail={agvDetail} />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -17,6 +17,7 @@ import { useWidgetStore } from "../../store/useWidgetStore";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { getUserData } from "../../functions/getUserData";
|
||||
import { useVersionContext } from "../builder/version/versionContext";
|
||||
import { useSceneContext } from "../scene/sceneContext";
|
||||
|
||||
type Side = "top" | "bottom" | "left" | "right";
|
||||
|
||||
@@ -28,6 +29,7 @@ type FormattedZoneData = Record<
|
||||
points: [];
|
||||
lockedPanels: Side[];
|
||||
zoneUuid: string;
|
||||
zoneName: string;
|
||||
zoneViewPortTarget: number[];
|
||||
zoneViewPortPosition: number[];
|
||||
widgets: Widget[];
|
||||
@@ -64,6 +66,9 @@ const RealTimeVisulization: React.FC = () => {
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { projectId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const { zoneStore } = useSceneContext();
|
||||
const { zones } = zoneStore();
|
||||
|
||||
|
||||
OuterClick({
|
||||
contextClassName: [
|
||||
@@ -82,6 +87,7 @@ const RealTimeVisulization: React.FC = () => {
|
||||
useEffect(() => {
|
||||
if (!projectId || !selectedVersion) return;
|
||||
getZone2dData(organization, projectId, selectedVersion?.versionId || '').then((response) => {
|
||||
// console.log('response: ', response);
|
||||
if (!response) return;
|
||||
// if (response.status === 401) {
|
||||
// console.log("force logout");
|
||||
@@ -94,19 +100,21 @@ const RealTimeVisulization: React.FC = () => {
|
||||
const formattedData = response.reduce<FormattedZoneData>(
|
||||
(acc, zone) => {
|
||||
|
||||
acc[zone.zoneName] = {
|
||||
acc[zone.zoneUuid] = {
|
||||
activeSides: [],
|
||||
panelOrder: [],
|
||||
lockedPanels: [],
|
||||
points: zone.points,
|
||||
zoneUuid: zone.zoneUuid,
|
||||
zoneViewPortTarget: zone.viewPortCenter,
|
||||
zoneViewPortPosition: zone.viewPortposition,
|
||||
zoneName: zone.zoneName,
|
||||
zoneViewPortTarget: zone.viewPortTarget,
|
||||
zoneViewPortPosition: zone.viewPortPosition,
|
||||
widgets: [],
|
||||
};
|
||||
return acc;
|
||||
}, {}
|
||||
);
|
||||
// console.log('formattedData: ', formattedData);
|
||||
setZonesData(formattedData);
|
||||
})
|
||||
|
||||
@@ -119,13 +127,14 @@ const RealTimeVisulization: React.FC = () => {
|
||||
if (!selectedZone) return prev;
|
||||
return {
|
||||
...prev,
|
||||
[selectedZone.zoneName]: {
|
||||
...prev[selectedZone.zoneName], // Keep existing properties
|
||||
[selectedZone.zoneUuid]: {
|
||||
...prev[selectedZone.zoneUuid], // Keep existing properties
|
||||
activeSides: selectedZone.activeSides || [],
|
||||
panelOrder: selectedZone.panelOrder || [],
|
||||
lockedPanels: selectedZone.lockedPanels || [],
|
||||
points: selectedZone.points || [],
|
||||
zoneUuid: selectedZone.zoneUuid || "",
|
||||
zoneName: selectedZone.zoneName || "",
|
||||
zoneViewPortTarget: selectedZone.zoneViewPortTarget || [],
|
||||
zoneViewPortPosition: selectedZone.zoneViewPortPosition || [],
|
||||
widgets: selectedZone.widgets || [],
|
||||
|
||||
@@ -34,6 +34,7 @@ interface DisplayZoneProps {
|
||||
points: [];
|
||||
widgets: Widget[];
|
||||
zoneUuid: string;
|
||||
zoneName: string;
|
||||
zoneViewPortTarget: number[];
|
||||
zoneViewPortPosition: number[];
|
||||
};
|
||||
@@ -111,8 +112,8 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
||||
setShowLeftArrow(isOverflowing && canScrollLeft);
|
||||
setShowRightArrow(isOverflowing && canScrollRight);
|
||||
|
||||
// console.log('canScrollRight: ', canScrollRight);
|
||||
// console.log('isOverflowing: ', isOverflowing);
|
||||
//
|
||||
//
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -180,9 +181,10 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
||||
// setSelectedChartId(null);
|
||||
|
||||
let response = await getSelect2dZoneData(zoneUuid, organization, projectId, selectedVersion?.versionId || '');
|
||||
// console.log('response2d: ', response);
|
||||
|
||||
//
|
||||
let res = await getFloatingZoneData(zoneUuid, organization, projectId, selectedVersion?.versionId || '');
|
||||
// console.log("resFloating: ", res);
|
||||
//
|
||||
|
||||
setFloatingWidget(res);
|
||||
// Set the selected zone in the store
|
||||
@@ -201,8 +203,8 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
||||
widgets: response.widgets || [],
|
||||
points: response.points || [],
|
||||
zoneUuid: zoneUuid,
|
||||
zoneViewPortTarget: response.viewPortCenter || {},
|
||||
zoneViewPortPosition: response.viewPortposition || {},
|
||||
zoneViewPortTarget: response.viewPortTarget || [],
|
||||
zoneViewPortPosition: response.viewPortPosition || [],
|
||||
});
|
||||
} catch (error) {
|
||||
echo.error("Failed to select zone");
|
||||
@@ -238,20 +240,22 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
|
||||
>
|
||||
{Object.keys(zonesData).length !== 0 ? (
|
||||
<>
|
||||
{Object.keys(zonesData).map((zoneName, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`zone ${selectedZone.zoneName === zoneName ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
{Object.values(zonesData).map((zone, index) => (
|
||||
<>
|
||||
{ }
|
||||
<div
|
||||
key={index}
|
||||
className={`zone ${selectedZone.zoneUuid === zone.zoneUuid ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
|
||||
console.log('zonesData: ', zonesData);
|
||||
handleSelect2dZoneData(zonesData[zoneName]?.zoneUuid, zoneName)
|
||||
}
|
||||
}
|
||||
>
|
||||
{zoneName}
|
||||
</div>
|
||||
handleSelect2dZoneData(zonesData[zone.zoneUuid]?.zoneUuid, zone.zoneName)
|
||||
}
|
||||
}
|
||||
>
|
||||
{zone.zoneName}
|
||||
</div>
|
||||
</>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
|
||||
export default function ZoneCentreTarget() {
|
||||
const { selectedZone } = useSelectedZoneStore();
|
||||
//
|
||||
const [previousZoneCentre, setPreviousZoneCentre] = useState<number[] | null>(
|
||||
null
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user