added avg paths

This commit is contained in:
Poovizhi99 2025-04-02 19:12:14 +05:30
parent d1e6b010e9
commit f5f74f35ad
7 changed files with 632 additions and 424 deletions
app/src

View File

@ -32,7 +32,10 @@ import {
useTransformMode, useTransformMode,
} from "../../store/store"; } from "../../store/store";
import useToggleStore from "../../store/useUIToggleStore"; import useToggleStore from "../../store/useUIToggleStore";
import { use3DWidget, useFloatingWidget } from "../../store/useDroppedObjectsStore"; import {
use3DWidget,
useFloatingWidget,
} from "../../store/useDroppedObjectsStore";
const Tools: React.FC = () => { const Tools: React.FC = () => {
const { templates } = useTemplateStore(); const { templates } = useTemplateStore();
@ -47,8 +50,8 @@ const Tools: React.FC = () => {
const { isPlaying, setIsPlaying } = usePlayButtonStore(); const { isPlaying, setIsPlaying } = usePlayButtonStore();
const { addTemplate } = useTemplateStore(); const { addTemplate } = useTemplateStore();
const { selectedZone } = useSelectedZoneStore(); const { selectedZone } = useSelectedZoneStore();
const { floatingWidget } = useFloatingWidget() const { floatingWidget } = useFloatingWidget();
const { widgets3D } = use3DWidget() const { widgets3D } = use3DWidget();
// wall options // wall options
const { toggleView, setToggleView } = useToggleView(); const { toggleView, setToggleView } = useToggleView();
@ -71,7 +74,7 @@ const Tools: React.FC = () => {
: true : true
); );
}, []); }, []);
useEffect(() => { }, [activeModule]); useEffect(() => {}, [activeModule]);
useEffect(() => { useEffect(() => {
setActiveTool(activeSubTool); setActiveTool(activeSubTool);
setActiveSubTool(activeSubTool); setActiveSubTool(activeSubTool);
@ -213,8 +216,9 @@ const Tools: React.FC = () => {
<div className="activeDropicon"> <div className="activeDropicon">
{activeSubTool == "cursor" && ( {activeSubTool == "cursor" && (
<div <div
className={`tool-button ${activeTool === "cursor" ? "active" : "" className={`tool-button ${
}`} activeTool === "cursor" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("cursor"); setActiveTool("cursor");
}} }}
@ -224,8 +228,9 @@ const Tools: React.FC = () => {
)} )}
{activeSubTool == "free-hand" && ( {activeSubTool == "free-hand" && (
<div <div
className={`tool-button ${activeTool === "free-hand" ? "active" : "" className={`tool-button ${
}`} activeTool === "free-hand" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("free-hand"); setActiveTool("free-hand");
}} }}
@ -235,8 +240,9 @@ const Tools: React.FC = () => {
)} )}
{activeSubTool == "delete" && ( {activeSubTool == "delete" && (
<div <div
className={`tool-button ${activeTool === "delete" ? "active" : "" className={`tool-button ${
}`} activeTool === "delete" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("delete"); setActiveTool("delete");
}} }}
@ -308,8 +314,9 @@ const Tools: React.FC = () => {
<div className="split"></div> <div className="split"></div>
<div className="draw-tools"> <div className="draw-tools">
<div <div
className={`tool-button ${activeTool === "draw-wall" ? "active" : "" className={`tool-button ${
}`} activeTool === "draw-wall" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("draw-wall"); setActiveTool("draw-wall");
}} }}
@ -318,8 +325,9 @@ const Tools: React.FC = () => {
<WallIcon isActive={activeTool === "draw-wall"} /> <WallIcon isActive={activeTool === "draw-wall"} />
</div> </div>
<div <div
className={`tool-button ${activeTool === "draw-zone" ? "active" : "" className={`tool-button ${
}`} activeTool === "draw-zone" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("draw-zone"); setActiveTool("draw-zone");
}} }}
@ -328,8 +336,9 @@ const Tools: React.FC = () => {
<ZoneIcon isActive={activeTool === "draw-zone"} /> <ZoneIcon isActive={activeTool === "draw-zone"} />
</div> </div>
<div <div
className={`tool-button ${activeTool === "draw-aisle" ? "active" : "" className={`tool-button ${
}`} activeTool === "draw-aisle" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("draw-aisle"); setActiveTool("draw-aisle");
}} }}
@ -338,8 +347,9 @@ const Tools: React.FC = () => {
<AsileIcon isActive={activeTool === "draw-aisle"} /> <AsileIcon isActive={activeTool === "draw-aisle"} />
</div> </div>
<div <div
className={`tool-button ${activeTool === "draw-floor" ? "active" : "" className={`tool-button ${
}`} activeTool === "draw-floor" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("draw-floor"); setActiveTool("draw-floor");
}} }}
@ -355,8 +365,9 @@ const Tools: React.FC = () => {
<div className="split"></div> <div className="split"></div>
<div className="draw-tools"> <div className="draw-tools">
<div <div
className={`tool-button ${activeTool === "measure" ? "active" : "" className={`tool-button ${
}`} activeTool === "measure" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("measure"); setActiveTool("measure");
}} }}
@ -372,8 +383,9 @@ const Tools: React.FC = () => {
<div className="split"></div> <div className="split"></div>
<div className="draw-tools"> <div className="draw-tools">
<div <div
className={`tool-button ${activeTool === "pen" ? "active" : "" className={`tool-button ${
}`} activeTool === "pen" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("pen"); setActiveTool("pen");
}} }}
@ -390,16 +402,14 @@ const Tools: React.FC = () => {
<div <div
className={`tool-button`} className={`tool-button`}
onClick={() => { onClick={() => {
handleSaveTemplate({ handleSaveTemplate({
addTemplate, addTemplate,
floatingWidget, floatingWidget,
widgets3D, widgets3D,
selectedZone, selectedZone,
templates, templates,
}) });
} }}
}
> >
<SaveTemplateIcon isActive={false} /> <SaveTemplateIcon isActive={false} />
</div> </div>
@ -409,8 +419,9 @@ const Tools: React.FC = () => {
<div className="split"></div> <div className="split"></div>
<div className="general-options"> <div className="general-options">
<div <div
className={`tool-button ${activeTool === "comment" ? "active" : "" className={`tool-button ${
}`} activeTool === "comment" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setActiveTool("comment"); setActiveTool("comment");
}} }}
@ -419,10 +430,12 @@ const Tools: React.FC = () => {
</div> </div>
{toggleThreeD && ( {toggleThreeD && (
<div <div
className={`tool-button ${activeTool === "play" ? "active" : "" className={`tool-button ${
}`} activeTool === "play" ? "active" : ""
}`}
onClick={() => { onClick={() => {
setIsPlaying(!isPlaying); setIsPlaying(!isPlaying);
setActiveTool("play");
}} }}
> >
<PlayIcon isActive={activeTool === "play"} /> <PlayIcon isActive={activeTool === "play"} />
@ -433,14 +446,19 @@ const Tools: React.FC = () => {
<> <>
<div className="split"></div> <div className="split"></div>
<div <div
className={`toggle-threed-button${toggleThreeD ? " toggled" : "" className={`toggle-threed-button${
}`} toggleThreeD ? " toggled" : ""
}`}
onClick={toggleSwitch} onClick={toggleSwitch}
> >
<div className={`toggle-option${!toggleThreeD ? " active" : ""}`}> <div
className={`toggle-option${!toggleThreeD ? " active" : ""}`}
>
2d 2d
</div> </div>
<div className={`toggle-option${toggleThreeD ? " active" : ""}`}> <div
className={`toggle-option${toggleThreeD ? " active" : ""}`}
>
3d 3d
</div> </div>
</div> </div>

View File

@ -5,22 +5,83 @@ import * as THREE from "three";
import * as Types from "../../../types/world/worldTypes"; import * as Types from "../../../types/world/worldTypes";
import PathNavigator from "./pathNavigator"; import PathNavigator from "./pathNavigator";
import NavMeshDetails from "./navMeshDetails"; import NavMeshDetails from "./navMeshDetails";
import {
useSelectedActionSphere,
useSimulationPaths,
} from "../../../store/store";
const Agv = ({ lines, plane }: { lines: Types.RefLines; plane: Types.RefMesh; }) => { const Agv = ({
const pathPoints = useMemo(() => [ lines,
[ plane,
{ x: 8.477161935339709, y: 0, z: 17.41343083550102 }, }: {
{ x: 9.175416491482693, y: 0, z: -12.361001232663693 }, lines: Types.RefLines;
], plane: Types.RefMesh;
// [ }) => {
// { x: 13.508213355232144, y: 0, z: -15.456970649652018 }, const [pathPoints, setPathPoints] = useState<
// { x: -30.464866520869617, y: 0, z: 9.779806557688929 }, {
// ], uuid: string;
[ points: { x: number; y: number; z: number }[];
{ x: 16.792040856420844, y: 0, z: 15.86281907549489 }, }[]
{ x: -42.77173264503395, y: 0, z: -15.821322764400804 }, >([]);
], const { simulationPaths } = useSimulationPaths();
], []); const { selectedActionSphere } = useSelectedActionSphere();
useEffect(() => {
if (!Array.isArray(simulationPaths)) {
} else {
let agvModels = simulationPaths.filter(
(val: any) => val.modelName === "agv"
);
let findMesh = agvModels.filter(
(val: any) =>
val.modeluuid === selectedActionSphere?.path?.modeluuid &&
val.type === "Vehicle"
);
const result =
findMesh.length > 0 &&
findMesh[0].type === "Vehicle" &&
typeof findMesh[0].point?.actions.start === "object" &&
typeof findMesh[0].point?.actions.end === "object" &&
"x" in findMesh[0].point.actions.start &&
"y" in findMesh[0].point.actions.start &&
"x" in findMesh[0].point.actions.end &&
"y" in findMesh[0].point.actions.end
? [
{
uuid: findMesh[0].modeluuid, // Ensure it's a number
points: [
{
x: findMesh[0].position[0],
y: findMesh[0].position[1],
z: findMesh[0].position[2],
},
{
x: findMesh[0].point.actions.start.x,
y: 0,
z: findMesh[0].point.actions.start.y,
},
{
x: findMesh[0].point.actions.end.x,
y: 0,
z: findMesh[0].point.actions.end.y,
},
],
},
]
: [];
if (result.length > 0) {
setPathPoints((prev) => {
const existingUUIDs = new Set(prev.map((item) => item.uuid));
const newItems = result.filter(
(item) => !existingUUIDs.has(item.uuid)
);
return [...prev, ...newItems];
});
}
}
}, [simulationPaths, selectedActionSphere]);
let groupRef = useRef() as Types.RefGroup; let groupRef = useRef() as Types.RefGroup;
const [navMesh, setNavMesh] = useState(); const [navMesh, setNavMesh] = useState();
@ -35,7 +96,12 @@ const Agv = ({ lines, plane }: { lines: Types.RefLines; plane: Types.RefMesh; })
plane={plane} plane={plane}
/> />
{pathPoints.map((pair, i) => ( {pathPoints.map((pair, i) => (
<PathNavigator navMesh={navMesh} selectedPoints={pair} key={i} /> <PathNavigator
navMesh={navMesh}
selectedPoints={pair.points}
id={pair.uuid}
key={i}
/>
))} ))}
<group ref={groupRef} visible={false} name="Meshes"></group> <group ref={groupRef} visible={false} name="Meshes"></group>
</> </>

View File

@ -35,10 +35,14 @@ export default function NavMeshDetails({
const [positions, indices] = getPositionsAndIndices(meshes); const [positions, indices] = getPositionsAndIndices(meshes);
const cs = 0.25; const cs = 0.25;
const ch = 0.5; const ch = 0.69;
const walkableRadius = 0.5; const walkableRadius = 0.5;
const { success, navMesh } = generateSoloNavMesh(positions, indices, { cs, ch, walkableRadius: Math.round(walkableRadius / ch), }); const { success, navMesh } = generateSoloNavMesh(positions, indices, {
cs,
ch,
walkableRadius: Math.round(walkableRadius / ch),
});
if (!success || !navMesh) { if (!success || !navMesh) {
return; return;
@ -49,7 +53,7 @@ export default function NavMeshDetails({
const debugDrawer = new DebugDrawer(); const debugDrawer = new DebugDrawer();
debugDrawer.drawNavMesh(navMesh); debugDrawer.drawNavMesh(navMesh);
// scene.add(debugDrawer); // scene.add(debugDrawer);
} catch (error) { } } catch (error) {}
}; };
initializeNavigation(); initializeNavigation();

View File

@ -1,98 +1,149 @@
import React, { useEffect, useState, useRef } from "react"; import React, { useEffect, useState, useRef } from "react";
import * as THREE from "three"; import * as THREE from "three";
import { useFrame } from "@react-three/fiber"; import { useFrame, useThree } from "@react-three/fiber";
import { NavMeshQuery } from "@recast-navigation/core"; import { NavMeshQuery } from "@recast-navigation/core";
import { Line } from "@react-three/drei"; import { Line } from "@react-three/drei";
import { useTh } from "leva/dist/declarations/src/styles";
import { useActiveTool } from "../../../store/store";
// Define interface for props // Define interface for props
interface PathNavigatorProps { interface PathNavigatorProps {
navMesh: any; navMesh: any;
selectedPoints: any; selectedPoints: any;
id: string;
} }
export default function PathNavigator({ export default function PathNavigator({
navMesh, navMesh,
selectedPoints, selectedPoints,
id,
}: PathNavigatorProps) { }: PathNavigatorProps) {
const [path, setPath] = useState<[number, number, number][]>([]); const [path, setPath] = useState<[number, number, number][]>([]);
const progressRef = useRef(0); const progressRef = useRef(0);
const distancesRef = useRef<number[]>([]);
const totalDistanceRef = useRef(0);
const currentSegmentIndex = useRef(0);
const { scene } = useThree();
const { activeTool } = useActiveTool();
const [startPoint, setStartPoint] = useState(new THREE.Vector3());
const meshRef = useRef<THREE.Mesh | null>(null); const meshRef = useRef<THREE.Mesh | null>(null);
useEffect(() => { useEffect(() => {
if (selectedPoints.length === 2 && navMesh) { if (!scene || !id || path.length < 2) return;
const [start, end] = selectedPoints;
if (!start || !end) return;
const navMeshQuery = new NavMeshQuery(navMesh); let totalDistance = 0;
const distances: number[] = [];
const { path: computedPath } = navMeshQuery.computePath(start, end); for (let i = 0; i < path.length - 1; i++) {
const start = new THREE.Vector3(...path[i]);
if (computedPath.length > 0) { const end = new THREE.Vector3(...path[i + 1]);
setPath(computedPath.map(({ x, y, z }) => [x, y + 0.1, z])); const segmentDistance = start.distanceTo(end);
progressRef.current = 0; distances.push(segmentDistance);
} totalDistance += segmentDistance;
} }
}, [selectedPoints, navMesh]);
distancesRef.current = distances;
totalDistanceRef.current = totalDistance;
progressRef.current = 0; // Reset progress when the path changes
}, [path]);
useEffect(() => {
if (!navMesh || selectedPoints.length === 0) return;
// Flatten the selectedPoints array into a single list of points
const allPoints = selectedPoints.flat();
// Compute paths between consecutive points
const computedPath: [number, number, number][] = [];
for (let i = 0; i < allPoints.length - 1; i++) {
const start = allPoints[i];
setStartPoint(
new THREE.Vector3(allPoints[0].x, allPoints[0].y, allPoints[0].z)
);
const end = allPoints[i + 1];
try {
const navMeshQuery = new NavMeshQuery(navMesh);
const { path: segmentPath } = navMeshQuery.computePath(start, end);
if (segmentPath && segmentPath.length > 0) {
computedPath.push(
...segmentPath.map(({ x, y, z }): [number, number, number] => [
x,
y + 0.1,
z,
])
);
}
} catch (error) {}
}
// Set the full computed path
if (computedPath.length > 0) {
setPath(computedPath);
currentSegmentIndex.current = 0; // Reset to the first segment
}
}, [selectedPoints, navMesh, path]);
useFrame((_, delta) => { useFrame((_, delta) => {
if (path.length > 1 && meshRef.current) { if (!scene || !id || path.length < 2) return;
const speed = 3;
progressRef.current += delta * speed;
let totalDistance = 0; // Find the object in the scene
const distances: number[] = []; const findObject = scene.getObjectByProperty("uuid", id);
for (let i = 0; i < path.length - 1; i++) { if (activeTool === "play") {
const start = new THREE.Vector3(...path[i]); if (!findObject) return;
const end = new THREE.Vector3(...path[i + 1]);
const segmentDistance = start.distanceTo(end); const speed = 5;
distances.push(segmentDistance); progressRef.current += delta * speed;
totalDistance += segmentDistance;
}
let coveredDistance = progressRef.current; let coveredDistance = progressRef.current;
let accumulatedDistance = 0; let accumulatedDistance = 0;
let index = 0; let index = 0;
// Determine the current segment of the path
while ( while (
index < distances.length && index < distancesRef.current.length &&
coveredDistance > accumulatedDistance + distances[index] coveredDistance > accumulatedDistance + distancesRef.current[index]
) { ) {
accumulatedDistance += distances[index]; accumulatedDistance += distancesRef.current[index];
index++; index++;
} }
if (index < distances.length) { // If the object has reached the end of the path, stop moving
const start = new THREE.Vector3(...path[index]); if (index >= distancesRef.current.length) {
const end = new THREE.Vector3(...path[index + 1]); progressRef.current = totalDistanceRef.current;
const segmentDistance = distances[index]; return;
const t = (coveredDistance - accumulatedDistance) / segmentDistance;
const position = start.clone().lerp(end, t); // Use clone() to avoid mutating the original vector
meshRef.current.position.copy(position);
const direction = new THREE.Vector3()
.subVectors(end, start)
.normalize();
const targetQuaternion = new THREE.Quaternion().setFromUnitVectors(
new THREE.Vector3(0, 0, 1),
direction
);
meshRef.current.quaternion.slerp(targetQuaternion, 0.1);
} else {
progressRef.current = totalDistance;
} }
// Interpolate position within the current segment
const start = new THREE.Vector3(...path[index]);
const end = new THREE.Vector3(...path[index + 1]);
const segmentDistance = distancesRef.current[index];
const t = (coveredDistance - accumulatedDistance) / segmentDistance;
const position = start.clone().lerp(end, t);
findObject.position.copy(position);
// Rotate the object to face the direction of movement
const direction = new THREE.Vector3().subVectors(end, start).normalize();
const targetQuaternion = new THREE.Quaternion().setFromUnitVectors(
new THREE.Vector3(0, 0, 1), // Assuming forward direction is (0, 0, 1)
direction
);
findObject.quaternion.slerp(targetQuaternion, 0.1); // Smoothly interpolate rotation
} else if (activeTool === "cursor") {
findObject?.position.copy(startPoint);
} }
}); });
return ( return (
<> <>
{/* {path.length > 0 && <Line points={path} color="blue" lineWidth={3} />} */} {path.length > 0 && <Line points={path} color="blue" lineWidth={3} />}
{path.length > 0 && ( {/* {path.length > 0 && (
<mesh ref={meshRef} position={path.length > 0 ? path[0] : [0, 0.1, 0]}> <mesh ref={meshRef} position={path.length > 0 ? path[0] : [0, 0.1, 0]}>
<boxGeometry args={[1, 1, 1]} /> <boxGeometry args={[1, 1, 1]} />
<meshNormalMaterial /> <meshNormalMaterial />
</mesh> </mesh>
)} )} */}
</> </>
); );
} }

View File

@ -52,6 +52,7 @@ import Layer2DVisibility from "../../builder/geomentries/layers/layer2DVisibilit
import DrieHtmlTemp from "../mqttTemp/drieHtmlTemp"; import DrieHtmlTemp from "../mqttTemp/drieHtmlTemp";
import ZoneGroup from "../../builder/groups/zoneGroup"; import ZoneGroup from "../../builder/groups/zoneGroup";
import Agv from "../../builder/agv/agv"; import Agv from "../../builder/agv/agv";
import useModuleStore from "../../../store/useModuleStore";
export default function World() { export default function World() {
const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements. const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements.
@ -120,6 +121,7 @@ export default function World() {
const { updateScene, setUpdateScene } = useUpdateScene(); const { updateScene, setUpdateScene } = useUpdateScene();
const { walls, setWalls } = useWalls(); const { walls, setWalls } = useWalls();
const { refTextupdate, setRefTextUpdate } = useRefTextUpdate(); const { refTextupdate, setRefTextUpdate } = useRefTextUpdate();
const { activeModule } = useModuleStore();
// const loader = new GLTFLoader(); // const loader = new GLTFLoader();
// const dracoLoader = new DRACOLoader(); // const dracoLoader = new DRACOLoader();
@ -359,8 +361,7 @@ export default function World() {
/> />
{/* <DrieHtmlTemp itemsGroup={itemsGroup} /> */} {/* <DrieHtmlTemp itemsGroup={itemsGroup} /> */}
{activeModule === "simulation" && <Agv lines={lines} plane={plane} />}
<Agv lines={lines} plane={plane} />
</> </>
); );
} }

View File

@ -1,299 +1,362 @@
import * as THREE from 'three'; import * as THREE from "three";
import * as Types from '../../../types/world/worldTypes'; import * as Types from "../../../types/world/worldTypes";
import { useRef, useState, useEffect, useMemo } from 'react'; import { useRef, useState, useEffect, useMemo } from "react";
import { Sphere, TransformControls } from '@react-three/drei'; import { Sphere, TransformControls } from "@react-three/drei";
import { useEditingPoint, useEyeDropMode, useIsConnecting, usePreviewPosition, useRenderDistance, useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../../store/store'; import {
import { useFrame, useThree } from '@react-three/fiber'; useEditingPoint,
import { useSubModuleStore } from '../../../store/useModuleStore'; useEyeDropMode,
useIsConnecting,
usePreviewPosition,
useRenderDistance,
useSelectedActionSphere,
useSelectedPath,
useSimulationPaths,
} from "../../../store/store";
import { useFrame, useThree } from "@react-three/fiber";
import { useSubModuleStore } from "../../../store/useModuleStore";
function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject<THREE.Group> }) { function PathCreation({
const { renderDistance } = useRenderDistance(); pathsGroupRef,
const { setSubModule } = useSubModuleStore(); }: {
const { setSelectedActionSphere, selectedActionSphere } = useSelectedActionSphere(); pathsGroupRef: React.MutableRefObject<THREE.Group>;
const { eyeDropMode, setEyeDropMode } = useEyeDropMode(); }) {
const { editingPoint, setEditingPoint } = useEditingPoint(); const { renderDistance } = useRenderDistance();
const { previewPosition, setPreviewPosition } = usePreviewPosition(); const { setSubModule } = useSubModuleStore();
const { raycaster, camera, pointer, gl } = useThree(); const { setSelectedActionSphere, selectedActionSphere } =
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); useSelectedActionSphere();
const { setSelectedPath } = useSelectedPath(); const { eyeDropMode, setEyeDropMode } = useEyeDropMode();
const { simulationPaths, setSimulationPaths } = useSimulationPaths(); const { editingPoint, setEditingPoint } = useEditingPoint();
const { isConnecting } = useIsConnecting(); const { previewPosition, setPreviewPosition } = usePreviewPosition();
const { raycaster, camera, pointer, gl } = useThree();
const groupRefs = useRef<{ [key: string]: THREE.Group }>({}); const plane = useMemo(
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); () => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0),
const isMovingRef = useRef(false); []
const transformRef = useRef<any>(null); );
const [transformMode, setTransformMode] = useState<'translate' | 'rotate' | null>(null); const { setSelectedPath } = useSelectedPath();
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const { isConnecting } = useIsConnecting();
useEffect(() => { const groupRefs = useRef<{ [key: string]: THREE.Group }>({});
setTransformMode(null); const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
const handleKeyDown = (e: KeyboardEvent) => { const isMovingRef = useRef(false);
if (!selectedActionSphere) return; const transformRef = useRef<any>(null);
if (e.key === 'g') { const [transformMode, setTransformMode] = useState<
setTransformMode(prev => prev === 'translate' ? null : 'translate'); "translate" | "rotate" | null
} >(null);
if (e.key === 'r') {
setTransformMode(prev => prev === 'rotate' ? null : 'rotate');
}
};
window.addEventListener('keydown', handleKeyDown); useEffect(() => {
return () => window.removeEventListener('keydown', handleKeyDown); setTransformMode(null);
}, [selectedActionSphere]); const handleKeyDown = (e: KeyboardEvent) => {
if (!selectedActionSphere) return;
useFrame(() => { if (e.key === "g") {
Object.values(groupRefs.current).forEach(group => { setTransformMode((prev) => (prev === "translate" ? null : "translate"));
if (group) { }
const distance = new THREE.Vector3(...group.position.toArray()).distanceTo(camera.position); if (e.key === "r") {
group.visible = distance <= renderDistance; setTransformMode((prev) => (prev === "rotate" ? null : "rotate"));
} }
});
});
const updateSimulationPaths = () => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => {
if (path.type === "Conveyor") {
return {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
position: [
selectedActionSphere.point.position.x,
selectedActionSphere.point.position.y,
selectedActionSphere.point.position.z,
],
rotation: [
selectedActionSphere.point.rotation.x,
selectedActionSphere.point.rotation.y,
selectedActionSphere.point.rotation.z,
]
}
: point
),
};
}
return path;
}) as Types.ConveyorEventsSchema[];
setSimulationPaths(updatedPaths);
}; };
useFrame(() => { window.addEventListener("keydown", handleKeyDown);
if (eyeDropMode) { return () => window.removeEventListener("keydown", handleKeyDown);
raycaster.setFromCamera(pointer, camera); }, [selectedActionSphere]);
const intersectionPoint = new THREE.Vector3();
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) { useFrame(() => {
setPreviewPosition({ x: point.x, y: point.z }); Object.values(groupRefs.current).forEach((group) => {
} if (group) {
} else { const distance = new THREE.Vector3(
setPreviewPosition(null); ...group.position.toArray()
).distanceTo(camera.position);
group.visible = distance <= renderDistance;
}
});
});
const updateSimulationPaths = () => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => {
if (path.type === "Conveyor") {
return {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
position: [
selectedActionSphere.point.position.x,
selectedActionSphere.point.position.y,
selectedActionSphere.point.position.z,
],
rotation: [
selectedActionSphere.point.rotation.x,
selectedActionSphere.point.rotation.y,
selectedActionSphere.point.rotation.z,
],
}
: point
),
};
}
return path;
}) as Types.ConveyorEventsSchema[];
setSimulationPaths(updatedPaths);
};
useFrame(() => {
if (eyeDropMode) {
raycaster.setFromCamera(pointer, camera);
const intersectionPoint = new THREE.Vector3();
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
setPreviewPosition({ x: point.x, y: point.z });
}
} else {
setPreviewPosition(null);
}
});
useEffect(() => {
if (!camera) return;
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
const onPointerDown = () => {
isMovingRef.current = false;
};
const onPointerMove = () => {
isMovingRef.current = true;
};
const onPointerUp = (event: PointerEvent) => {
if (
!isMovingRef.current &&
eyeDropMode &&
event.button === 0 &&
previewPosition
) {
event.preventDefault();
if (editingPoint) {
handlePointUpdate(editingPoint, previewPosition.x, previewPosition.y);
setEditingPoint(null);
setEyeDropMode(false);
} }
}
};
if (eyeDropMode) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
}
return () => {
canvasElement.removeEventListener("pointerdown", onPointerDown);
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
};
}, [eyeDropMode, editingPoint, previewPosition]);
const handlePointUpdate = (
pointType: "start" | "end",
x: number,
z: number
) => {
if (!selectedActionSphere?.point?.uuid) return;
const updatedPaths = simulationPaths.map((path) => {
if (
path.type === "Vehicle" &&
path.point.uuid === selectedActionSphere.point.uuid
) {
return {
...path,
point: {
...path.point,
actions: {
...path.point.actions,
[pointType]: {
...path.point.actions[pointType],
x: x,
y: z,
},
},
},
};
}
return path;
}); });
useEffect(() => { setSimulationPaths(updatedPaths);
if (!camera) return; };
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
return (
<group name="simulation-simulationPaths-group" ref={pathsGroupRef}>
{simulationPaths.map((path) => {
if (path.type === "Conveyor") {
const points = path.points.map(
(point) => new THREE.Vector3(...point.position)
);
const onPointerDown = () => { return (
isMovingRef.current = false; <group
}; name={`${path.modeluuid}-event-path`}
key={path.modeluuid}
const onPointerMove = () => { ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
isMovingRef.current = true; position={path.position}
}; rotation={path.rotation}
onClick={(e) => {
const onPointerUp = (event: PointerEvent) => { if (isConnecting || eyeDropMode) return;
if (!isMovingRef.current && eyeDropMode && event.button === 0 && previewPosition) { e.stopPropagation();
event.preventDefault(); setSelectedPath({
if (editingPoint) { path,
handlePointUpdate(editingPoint, previewPosition.x, previewPosition.y); group: groupRefs.current[path.modeluuid],
setEditingPoint(null); });
setEyeDropMode(false); setSelectedActionSphere(null);
} setTransformMode(null);
} setSubModule("mechanics");
}; }}
onPointerMissed={() => {
if (eyeDropMode) { if (eyeDropMode) return;
canvasElement.addEventListener("pointerdown", onPointerDown); setSelectedPath(null);
canvasElement.addEventListener("pointermove", onPointerMove); setSubModule("properties");
canvasElement.addEventListener("pointerup", onPointerUp); }}
} >
{path.points.map((point, index) => (
return () => { <Sphere
canvasElement.removeEventListener("pointerdown", onPointerDown); key={point.uuid}
canvasElement.removeEventListener("pointermove", onPointerMove); uuid={point.uuid}
canvasElement.removeEventListener("pointerup", onPointerUp); position={point.position}
}; args={[0.15, 32, 32]}
}, [eyeDropMode, editingPoint, previewPosition]); name="events-sphere"
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
const handlePointUpdate = (pointType: 'start' | 'end', x: number, z: number) => { onClick={(e) => {
if (!selectedActionSphere?.point?.uuid) return; if (isConnecting || eyeDropMode) return;
e.stopPropagation();
const updatedPaths = simulationPaths.map((path) => { setSelectedActionSphere({
if (path.type === "Vehicle" && path.point.uuid === selectedActionSphere.point.uuid) { path,
return { point: sphereRefs.current[point.uuid],
...path, });
point: { setSubModule("mechanics");
...path.point, setSelectedPath(null);
actions: { }}
...path.point.actions, userData={{ point, path }}
[pointType]: { onPointerMissed={() => {
...path.point.actions[pointType], if (eyeDropMode) return;
x: x, setSubModule("properties");
y: z setSelectedActionSphere(null);
} }}
} >
<meshStandardMaterial
color={
index === 0
? "orange"
: index === path.points.length - 1
? "blue"
: "green"
} }
}; />
} </Sphere>
return path; ))}
});
setSimulationPaths(updatedPaths); {points.slice(0, -1).map((point, index) => {
}; const nextPoint = points[index + 1];
const segmentCurve = new THREE.CatmullRomCurve3([
point,
nextPoint,
]);
const tubeGeometry = new THREE.TubeGeometry(
segmentCurve,
20,
0.1,
16,
false
);
return ( return (
<group name='simulation-simulationPaths-group' ref={pathsGroupRef}> <mesh
{simulationPaths.map((path) => { name="event-connection-tube"
if (path.type === 'Conveyor') { key={`tube-${index}`}
const points = path.points.map(point => new THREE.Vector3(...point.position)); geometry={tubeGeometry}
>
<meshStandardMaterial
transparent
opacity={0.9}
color="red"
/>
</mesh>
);
})}
</group>
);
} else if (path.type === "Vehicle") {
return (
<group
name={`${path.modeluuid}-vehicle-path`}
key={path.modeluuid}
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
position={path.position}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedPath({
path,
group: groupRefs.current[path.modeluuid],
});
setSelectedActionSphere(null);
setTransformMode(null);
setSubModule("mechanics");
}}
onPointerMissed={() => {
if (eyeDropMode) return;
setSelectedPath(null);
setSubModule("properties");
}}
>
<Sphere
key={path.point.uuid}
uuid={path.point.uuid}
position={path.point.position}
args={[0.15, 32, 32]}
name="events-sphere"
ref={(el) => (sphereRefs.current[path.point.uuid] = el!)}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedActionSphere({
path,
point: sphereRefs.current[path.point.uuid],
});
setSubModule("mechanics");
setSelectedPath(null);
}}
userData={{ point: path.point, path }}
onPointerMissed={() => {
if (eyeDropMode) return;
setSubModule("properties");
setSelectedActionSphere(null);
}}
>
<meshStandardMaterial color="purple" />
</Sphere>
</group>
);
}
return null;
})}
return ( {selectedActionSphere && transformMode && (
<group <TransformControls
name={`${path.modeluuid}-event-path`} ref={transformRef}
key={path.modeluuid} object={selectedActionSphere.point}
ref={el => (groupRefs.current[path.modeluuid] = el!)} mode={transformMode}
position={path.position} onMouseUp={updateSimulationPaths}
rotation={path.rotation} />
onClick={(e) => { )}
if (isConnecting || eyeDropMode) return; </group>
e.stopPropagation(); );
setSelectedPath({ path, group: groupRefs.current[path.modeluuid] });
setSelectedActionSphere(null);
setTransformMode(null);
setSubModule('mechanics');
}}
onPointerMissed={() => {
if (eyeDropMode) return;
setSelectedPath(null);
setSubModule('properties');
}}
>
{path.points.map((point, index) => (
<Sphere
key={point.uuid}
uuid={point.uuid}
position={point.position}
args={[0.15, 32, 32]}
name='events-sphere'
ref={el => (sphereRefs.current[point.uuid] = el!)}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedActionSphere({
path,
point: sphereRefs.current[point.uuid]
});
setSubModule('mechanics');
setSelectedPath(null);
}}
userData={{ point, path }}
onPointerMissed={() => {
if (eyeDropMode) return;
setSubModule('properties');
setSelectedActionSphere(null);
}}
>
<meshStandardMaterial
color={index === 0 ? 'orange' : index === path.points.length - 1 ? 'blue' : 'green'}
/>
</Sphere>
))}
{points.slice(0, -1).map((point, index) => {
const nextPoint = points[index + 1];
const segmentCurve = new THREE.CatmullRomCurve3([point, nextPoint]);
const tubeGeometry = new THREE.TubeGeometry(segmentCurve, 20, 0.1, 16, false);
return (
<mesh name='event-connection-tube' key={`tube-${index}`} geometry={tubeGeometry}>
<meshStandardMaterial transparent opacity={0.9} color="red" />
</mesh>
);
})}
</group>
);
} else if (path.type === 'Vehicle') {
return (
<group
name={`${path.modeluuid}-vehicle-path`}
key={path.modeluuid}
ref={el => (groupRefs.current[path.modeluuid] = el!)}
position={path.position}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedPath({ path, group: groupRefs.current[path.modeluuid] });
setSelectedActionSphere(null);
setTransformMode(null);
setSubModule('mechanics');
}}
onPointerMissed={() => {
if (eyeDropMode) return;
setSelectedPath(null);
setSubModule('properties');
}}
>
<Sphere
key={path.point.uuid}
uuid={path.point.uuid}
position={path.point.position}
args={[0.15, 32, 32]}
name='events-sphere'
ref={el => (sphereRefs.current[path.point.uuid] = el!)}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedActionSphere({
path,
point: sphereRefs.current[path.point.uuid]
});
setSubModule('mechanics');
setSelectedPath(null);
}}
userData={{ point: path.point, path }}
onPointerMissed={() => {
if (eyeDropMode) return;
setSubModule('properties');
setSelectedActionSphere(null);
}}
>
<meshStandardMaterial color="purple" />
</Sphere>
</group>
);
}
return null;
})}
{selectedActionSphere && transformMode && (
<TransformControls
ref={transformRef}
object={selectedActionSphere.point}
mode={transformMode}
onMouseUp={updateSimulationPaths}
/>
)}
</group>
);
} }
export default PathCreation; export default PathCreation;

View File

@ -1,47 +1,52 @@
import { useState, useEffect, useRef, useMemo } from 'react'; import { useState, useEffect, useRef, useMemo } from "react";
import { useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../store/store'; import {
import * as THREE from 'three'; useSelectedActionSphere,
import Behaviour from './behaviour/behaviour'; useSelectedPath,
import PathCreation from './path/pathCreation'; useSimulationPaths,
import PathConnector from './path/pathConnector'; } from "../../store/store";
import useModuleStore from '../../store/useModuleStore'; import * as THREE from "three";
import ProcessContainer from './process/processContainer'; import Behaviour from "./behaviour/behaviour";
import PathCreation from "./path/pathCreation";
import PathConnector from "./path/pathConnector";
import useModuleStore from "../../store/useModuleStore";
import ProcessContainer from "./process/processContainer";
import Agv from "../builder/agv/agv";
function Simulation() { function Simulation() {
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const pathsGroupRef = useRef() as React.MutableRefObject<THREE.Group>; const pathsGroupRef = useRef() as React.MutableRefObject<THREE.Group>;
const { simulationPaths, setSimulationPaths } = useSimulationPaths(); const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const [processes, setProcesses] = useState([]); const [processes, setProcesses] = useState([]);
useEffect(() => { useEffect(() => {
// console.log('simulationPaths: ', simulationPaths); // console.log('simulationPaths: ', simulationPaths);
}, [simulationPaths]); }, [simulationPaths]);
// useEffect(() => { // useEffect(() => {
// if (selectedActionSphere) { // if (selectedActionSphere) {
// console.log('selectedActionSphere: ', selectedActionSphere); // console.log('selectedActionSphere: ', selectedActionSphere);
// } // }
// }, [selectedActionSphere]); // }, [selectedActionSphere]);
// useEffect(() => { // useEffect(() => {
// if (selectedPath) { // if (selectedPath) {
// console.log('selectedPath: ', selectedPath); // console.log('selectedPath: ', selectedPath);
// } // }
// }, [selectedPath]); // }, [selectedPath]);
return (
return ( <>
<Behaviour />
{activeModule === "simulation" && (
<> <>
<Behaviour /> <PathCreation pathsGroupRef={pathsGroupRef} />
{activeModule === 'simulation' && ( <PathConnector pathsGroupRef={pathsGroupRef} />
<> <ProcessContainer />
<PathCreation pathsGroupRef={pathsGroupRef} /> {/* <Agv /> */}
<PathConnector pathsGroupRef={pathsGroupRef} />
<ProcessContainer />
</>
)}
</> </>
); )}
</>
);
} }
export default Simulation; export default Simulation;