created paths based on aisle and wall points

This commit is contained in:
2025-03-27 15:37:16 +05:30
parent 6e925a995c
commit c1251dc598
5 changed files with 141 additions and 1827 deletions

View File

@@ -39,7 +39,7 @@ const DropDownList: React.FC<DropDownListProps> = ({
useEffect(() => { useEffect(() => {
async function GetZoneData() { async function GetZoneData() {
const response = await getZonesApi("hexrfactory") const response = await getZonesApi("hexrfactory")
console.log('response: ', response.data); // console.log('response: ', response.data);
setZoneDataList([{ id: "1", name: "zone1" }, setZoneDataList([{ id: "1", name: "zone1" },
{ id: "2", name: "Zone 2" },]) { id: "2", name: "Zone 2" },])
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,146 +1,43 @@
// import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
// import { init as initRecastNavigation } from "@recast-navigation/core";
// import { generateSoloNavMesh } from "@recast-navigation/generators";
// import { DebugDrawer, getPositionsAndIndices } from "@recast-navigation/three";
// import { useThree } from "@react-three/fiber";
// import * as THREE from "three";
// interface RawNavMesh {
// ptr: number; // Replace `number` with the actual type if known
// }
// interface NavMesh {
// raw: RawNavMesh;
// }
// // Update the MeshState interface to use the correct type
// interface MeshState {
// setNavMesh: React.Dispatch<React.SetStateAction<NavMesh | null>>;
// }
// export default function NavMeshDetails({ setNavMesh }: MeshState) {
// const { scene } = useThree();
// useEffect(() => {
// const initializeNavMesh = async () => {
// try {
// // Initialize Recast Navigation
// await initRecastNavigation();
// // Extract meshes from the scene
// // Extract meshes from the scene
// const meshes = scene?.children
// .filter((child) => child.name === "Meshes")
// .flatMap((mesh) => mesh.children);
// if (!meshes || meshes.length === 0) {
// return;
// }
// // Filter and process only Mesh objects
// const meshObjects = meshes.filter(
// (
// child
// ): child is THREE.Mesh<
// THREE.BufferGeometry,
// THREE.Material | THREE.Material[]
// > => child instanceof THREE.Mesh
// );
// if (meshObjects.length === 0) {
// return;
// }
// // Get positions and indices from the meshes
// const [positions, indices] = getPositionsAndIndices(meshObjects);
// // Generate navigation mesh
// const cs = 0.05;
// const ch = 0.05;
// const walkableRadius = 0.87;
// const { success, navMesh } = generateSoloNavMesh(positions, indices, {
// cs,
// ch,
// walkableRadius: Math.round(walkableRadius / ch),
// });
// if (!success || !navMesh) {
// return;
// }
// // Log and update the navigation mesh
//
// setNavMesh(navMesh);
// // Draw the debug visualization
// const debugDrawer = new DebugDrawer();
// debugDrawer.drawNavMesh(navMesh);
// // scene.add(debugDrawer); // Uncomment if you want to add the debug drawer to the scene
// } catch (error) {
// }
// };
// initializeNavMesh();
// }, [setNavMesh, scene]);
// return null;
// }
import React, { useEffect } from "react";
import { init as initRecastNavigation } from "@recast-navigation/core"; import { init as initRecastNavigation } from "@recast-navigation/core";
import { generateSoloNavMesh } from "@recast-navigation/generators"; import { generateSoloNavMesh } from "@recast-navigation/generators";
import { DebugDrawer, getPositionsAndIndices } from "@recast-navigation/three"; import { DebugDrawer, getPositionsAndIndices } from "@recast-navigation/three";
import { useThree } from "@react-three/fiber"; import { useThree } from "@react-three/fiber";
import * as THREE from "three"; import * as THREE from "three";
import * as Types from "../../../types/world/worldTypes";
// Import the NavMesh type from the library interface NavMeshDetailsProps {
import { NavMesh as RecastNavMesh } from "@recast-navigation/core"; setNavMesh: (navMesh: any) => void;
groupRef: React.MutableRefObject<THREE.Group | null>;
// Define the state type based on the library's NavMesh type lines: Types.RefLines;
interface MeshState { plane: Types.RefMesh;
setNavMesh: React.Dispatch<React.SetStateAction<RecastNavMesh | null>>;
} }
export default function NavMeshDetails({ setNavMesh }: MeshState) { export default function NavMeshDetails({
lines,
setNavMesh,
groupRef,
plane,
}: NavMeshDetailsProps) {
const { scene } = useThree(); const { scene } = useThree();
useEffect(() => { useEffect(() => {
const initializeNavMesh = async () => { const initializeNavigation = async () => {
try { try {
// Initialize Recast Navigation
await initRecastNavigation(); await initRecastNavigation();
// Extract meshes from the scene if (!groupRef.current || groupRef.current.children.length === 0) {
const meshes = scene?.children
.filter((child) => child.name === "Meshes")
.flatMap((mesh) => mesh.children);
if (!meshes || meshes.length === 0) {
return; return;
} }
// Filter and process only Mesh objects const meshes = groupRef?.current?.children as THREE.Mesh[];
const meshObjects = meshes.filter(
(
child
): child is THREE.Mesh<
THREE.BufferGeometry,
THREE.Material | THREE.Material[]
> => child instanceof THREE.Mesh
);
if (meshObjects.length === 0) { const [positions, indices] = getPositionsAndIndices(meshes);
return;
}
// Get positions and indices from the meshes const cs = 0.5;
const [positions, indices] = getPositionsAndIndices(meshObjects); const ch = 0.5;
const walkableRadius = 0.89;
// Generate navigation mesh
const cs = 0.05;
const ch = 0.05;
const walkableRadius = 0.95;
const { success, navMesh } = generateSoloNavMesh(positions, indices, { const { success, navMesh } = generateSoloNavMesh(positions, indices, {
cs, cs,
ch, ch,
@@ -150,17 +47,17 @@ export default function NavMeshDetails({ setNavMesh }: MeshState) {
if (!success || !navMesh) { if (!success || !navMesh) {
return; return;
} }
// Log and update the navigation mesh
setNavMesh(navMesh); // Now compatible with the library's NavMesh type setNavMesh(navMesh);
// Draw the debug visualization
const debugDrawer = new DebugDrawer(); const debugDrawer = new DebugDrawer();
debugDrawer.drawNavMesh(navMesh); debugDrawer.drawNavMesh(navMesh);
// scene.add(debugDrawer); // Uncomment if you want to add the debug drawer to the scene // scene.add(debugDrawer);
} catch (error) {} } catch (error) {}
}; };
initializeNavMesh(); initializeNavigation();
}, [setNavMesh, scene]); }, [scene, groupRef, lines.current]);
return null; return null;
} }

View File

@@ -1,76 +1,46 @@
import React, { useCallback, useEffect, useRef, useState } 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 { NavMeshQuery } from "@recast-navigation/core"; import { NavMeshQuery } from "@recast-navigation/core";
import { NavMesh as RecastNavMesh } from "@recast-navigation/core";
import { useFrame, useThree } from "@react-three/fiber";
import { Line } from "@react-three/drei"; import { Line } from "@react-three/drei";
interface Pair {
x: number; // Define interface for props
y: number; interface PathNavigatorProps {
z: number; navMesh: any;
selectedPoints: any;
} }
interface PathProps {
navMesh: RecastNavMesh | null; // The navigation mesh
pathPoints: Pair[] | undefined; // Array of points (or undefined)
}
const PathNavigator = ({ navMesh, pathPoints }: PathProps) => {
const { scene, raycaster, gl } = useThree();
const [path, setPath] = useState<THREE.Vector3[]>([]); // Path is an array of THREE.Vector3 export default function PathNavigator({
const [points, setSelectedPoints] = useState<THREE.Vector3[]>([]); // Path is an array of THREE.Vector3 navMesh,
const progressRef = useRef<number>(0); selectedPoints,
const meshRef = useRef<THREE.Mesh>(null!); }: PathNavigatorProps) {
const handleClick = useCallback(() => { const [path, setPath] = useState<[number, number, number][]>([]);
if (!navMesh) return; const progressRef = useRef(0);
const meshRef = useRef<THREE.Mesh | null>(null);
const intersects = raycaster.intersectObjects(scene.children, true); useEffect(() => {
if (intersects.length > 0) { if (selectedPoints.length === 2 && navMesh) {
const { point } = intersects[0]; const [start, end] = selectedPoints;
const newPoint = { x: point.x, y: 0, z: point.z }; if (!start || !end) return;
setSelectedPoints((prevPoints: THREE.Vector3[]) => {
if (prevPoints.length === 2) {
// If two points already exist, replace them with the new point
return [new THREE.Vector3(newPoint.x, newPoint.y, newPoint.z)];
}
// Otherwise, append the new point to the array
return [
...prevPoints,
new THREE.Vector3(newPoint.x, newPoint.y, newPoint.z),
];
});
}
}, [navMesh, scene]);
React.useEffect(() => {
if (points?.length === 2 && navMesh) {
const [start, end] = points;
console.log("start: ", start);
console.log("end: ", end);
const navMeshQuery = new NavMeshQuery(navMesh); const navMeshQuery = new NavMeshQuery(navMesh);
console.log("navMeshQuery: ", navMeshQuery);
const { path } = navMeshQuery.computePath(start, end); const { path: computedPath } = navMeshQuery.computePath(start, end);
console.log("paths: ", path);
// if (path.length > 0) { if (computedPath.length > 0) {
// setPath( setPath(computedPath.map(({ x, y, z }) => [x, y + 0.1, z]));
// path.map((point) => { progressRef.current = 0;
// const newY = point.y + 0.1; // Increment the y-coordinate
// return new THREE.Vector3(point.x, newY, point.z); // Create a new Vector3
// })
// );
// progressRef.current = 0;
// }
} }
}, [points,]); }
}, [selectedPoints, navMesh]);
useFrame((_, delta) => { useFrame((_, delta) => {
if (path.length > 1 && meshRef.current) { if (path.length > 1 && meshRef.current) {
const speed = 3; const speed = 3;
progressRef.current += delta * speed; progressRef.current += delta * speed;
let totalDistance = 0; let totalDistance = 0;
const distances = []; const distances: number[] = [];
for (let i = 0; i < path.length - 1; i++) { for (let i = 0; i < path.length - 1; i++) {
const start = new THREE.Vector3(...path[i]); const start = new THREE.Vector3(...path[i]);
const end = new THREE.Vector3(...path[i + 1]); const end = new THREE.Vector3(...path[i + 1]);
@@ -97,7 +67,7 @@ const PathNavigator = ({ navMesh, pathPoints }: PathProps) => {
const segmentDistance = distances[index]; const segmentDistance = distances[index];
const t = (coveredDistance - accumulatedDistance) / segmentDistance; const t = (coveredDistance - accumulatedDistance) / segmentDistance;
const position = start.lerp(end, t); const position = start.clone().lerp(end, t); // Use clone() to avoid mutating the original vector
meshRef.current.position.copy(position); meshRef.current.position.copy(position);
const direction = new THREE.Vector3() const direction = new THREE.Vector3()
@@ -114,20 +84,10 @@ const PathNavigator = ({ navMesh, pathPoints }: PathProps) => {
} }
}); });
useEffect(() => {
gl.domElement.addEventListener("click", handleClick);
return () => gl.domElement.removeEventListener("click", handleClick);
}, [handleClick]);
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 && (
// <primitive
// ref={gltfRef}
// object={gltfClone}
// position={path.length > 0 ? path[0] : [0, 0.1, 0]}
// scale={[0.5, 0.5, 0.5]}
// />
<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 />
@@ -135,6 +95,4 @@ const PathNavigator = ({ navMesh, pathPoints }: PathProps) => {
)} )}
</> </>
); );
}; }
export default PathNavigator;

View File

@@ -1,86 +1,86 @@
import * as THREE from "three"; import * as THREE from "three";
import { useEffect, useRef } from "react"; import { useEffect, useState } from "react";
import { useThree } from "@react-three/fiber";
import * as turf from "@turf/turf"; import * as turf from "@turf/turf";
import * as Types from "../../../types/world/worldTypes"; import * as Types from "../../../types/world/worldTypes";
// import { Feature, Polygon, MultiPolygon } from "@turf/helpers"; import arrayLinesToObject from "../geomentries/lines/lineConvertions/arrayLinesToObject";
type Point = { interface PolygonGeneratorProps {
position: { x: number; y: number; z: number }; groupRef: React.MutableRefObject<THREE.Group | null>;
uuid: string; lines: Types.RefLines;
}; plane: Types.RefMesh;
}
type LineData = {
type: string;
line: Point[];
_id: {};
layer: number;
__v: number;
};
type PolygonGeneratorProps = {
processPoint: LineData[];
groupRef: React.RefObject<THREE.Group>;
lines: any;
plane: any;
};
export default function PolygonGenerator({ export default function PolygonGenerator({
processPoint,
groupRef, groupRef,
lines, lines,
plane, plane,
}: PolygonGeneratorProps) { }: PolygonGeneratorProps) {
const { scene } = useThree(); // const [rooms, setRooms] = useState<THREE.Vector3[][]>([]);
useEffect(() => { useEffect(() => {
if (!processPoint) return; if (groupRef.current && plane.current) {
const wallInLayer = processPoint?.filter( groupRef.current.add(plane.current.clone());
(line) => line.type === "WallLine" }
); }, [groupRef, plane]);
const wallPoints = wallInLayer.map((pair) =>
pair?.line.map((vals) => vals.position)
);
renderWallGeometry(wallPoints);
const linesInLayer = processPoint?.filter( useEffect(() => {
(line) => line.type === "AisleLine" let allLines = arrayLinesToObject(lines.current);
); const wallLines = allLines?.filter((line) => line?.type === "WallLine");
const result = linesInLayer.map((pair) => const aisleLines = allLines?.filter((line) => line?.type === "AisleLine");
const wallPoints = wallLines
.map((pair) => pair?.line.map((vals) => vals.position))
.filter((wall): wall is THREE.Vector3[] => !!wall);
const result = aisleLines.map((pair) =>
pair?.line.map((point) => ({ pair?.line.map((point) => ({
position: [point.position.x, point.position.z], position: [point.position.x, point.position.z],
uuid: point.uuid, uuid: point.uuid,
})) }))
); );
if (!result || result.some((line) => !line)) {
return;
}
const lineFeatures = result.map((line) => const lineFeatures = result?.map((line: any) =>
turf.lineString(line.map((p) => p.position)) turf.lineString(line.map((p: any) => p?.position))
); );
const polygons = turf.polygonize(turf.featureCollection(lineFeatures)); const polygons = turf.polygonize(turf.featureCollection(lineFeatures));
let union: any[] = []; renderWallGeometry(wallPoints);
let union: any = [];
polygons.features.forEach((feature) => { polygons.features.forEach((feature) => {
union.push(feature); union.push(feature);
}); });
if (union.length > 0) { if (union.length > 1) {
const unionResult = turf.union(turf.featureCollection(union)); const unionResult = turf.union(turf.featureCollection(union));
if (unionResult && unionResult.geometry.type === "MultiPolygon") {
if (unionResult?.geometry.type === "MultiPolygon") {
unionResult.geometry.coordinates.forEach((poly) => { unionResult.geometry.coordinates.forEach((poly) => {
const Coordinates = poly[0].map( const coordinates = poly[0].map(([x, z]) => {
([x, z]) => new THREE.Vector3(x, 0, z) return new THREE.Vector3(x, 0, z);
);
renderBoxGeometry(Coordinates);
}); });
} else if (unionResult && unionResult.geometry.type === "Polygon") { renderBoxGeometry(coordinates);
const Coordinates = unionResult.geometry.coordinates[0].map( });
([x, z]) => new THREE.Vector3(x, 0, z) } else if (unionResult?.geometry.type === "Polygon") {
const coordinates = unionResult.geometry.coordinates[0].map(
([x, z]) => {
return new THREE.Vector3(x, 0, z);
}
); );
renderBoxGeometry(Coordinates); renderBoxGeometry(coordinates);
} }
} else if (union.length === 1) {
const coordinates = union[0].geometry.coordinates[0].map(
([x, z]: [number, number]) => {
return new THREE.Vector3(x, 0, z);
} }
}, [processPoint]); );
// setRooms((prevRooms) => [...prevRooms, coordinates]);
}
}, [lines.current]);
const renderBoxGeometry = (coordinates: THREE.Vector3[]) => { const renderBoxGeometry = (coordinates: THREE.Vector3[]) => {
const minX = Math.min(...coordinates.map((p) => p.x)); const minX = Math.min(...coordinates.map((p) => p.x));
@@ -97,16 +97,13 @@ export default function PolygonGenerator({
color: "#ff66cc", color: "#ff66cc",
visible: false, visible: false,
}); });
const mesh = new THREE.Mesh(geometry, material); const mesh = new THREE.Mesh(geometry, material);
mesh.position.set((minX + maxX) / 2, height / 2, (minZ + maxZ) / 2); mesh.position.set((minX + maxX) / 2, height / 2, (minZ + maxZ) / 2);
groupRef.current?.add(mesh);
groupRef?.current?.add(mesh);
// scene.add(groupRef.current!);
}; };
const renderWallGeometry = ( const renderWallGeometry = (walls: THREE.Vector3[][]) => {
walls: { x: number; y: number; z: number }[][]
) => {
walls.forEach((wall) => { walls.forEach((wall) => {
if (wall.length < 2) return; if (wall.length < 2) return;
@@ -117,19 +114,20 @@ export default function PolygonGenerator({
wall[i + 1].y, wall[i + 1].y,
wall[i + 1].z wall[i + 1].z
); );
const wallHeight = 10; const wallHeight = 10;
const direction = new THREE.Vector3().subVectors(end, start); const direction = new THREE.Vector3().subVectors(end, start);
const length = direction.length(); const length = direction.length();
direction.normalize(); direction.normalize();
const wallGeometry = new THREE.BoxGeometry(length, wallHeight, 0.5); const wallGeometry = new THREE.BoxGeometry(length, wallHeight);
const wallMaterial = new THREE.MeshBasicMaterial({ const wallMaterial = new THREE.MeshBasicMaterial({
color: "#aaa", color: "#aaa",
transparent: true, transparent: true,
opacity: 0.5, opacity: 0.5,
}); });
const wallMesh = new THREE.Mesh(wallGeometry, wallMaterial);
const wallMesh = new THREE.Mesh(wallGeometry, wallMaterial);
const midPoint = new THREE.Vector3() const midPoint = new THREE.Vector3()
.addVectors(start, end) .addVectors(start, end)
.multiplyScalar(0.5); .multiplyScalar(0.5);
@@ -139,17 +137,7 @@ export default function PolygonGenerator({
quaternion.setFromUnitVectors(new THREE.Vector3(1, 0, 0), direction); quaternion.setFromUnitVectors(new THREE.Vector3(1, 0, 0), direction);
wallMesh.quaternion.copy(quaternion); wallMesh.quaternion.copy(quaternion);
groupRef?.current?.add(wallMesh); groupRef.current?.add(wallMesh);
// scene.add(groupRef.current!);
const lineGeometry = new THREE.BufferGeometry().setFromPoints([
start,
end,
]);
const lineMaterial = new THREE.LineBasicMaterial({ color: "blue" });
const line = new THREE.Line(lineGeometry, lineMaterial);
groupRef?.current?.add(line);
} }
}); });
}; };