Merge branch 'main' into simulation-animation
This commit is contained in:
@@ -5,22 +5,83 @@ import * as THREE from "three";
|
||||
import * as Types from "../../../types/world/worldTypes";
|
||||
import PathNavigator from "./pathNavigator";
|
||||
import NavMeshDetails from "./navMeshDetails";
|
||||
import {
|
||||
useSelectedActionSphere,
|
||||
useSimulationPaths,
|
||||
} from "../../../store/store";
|
||||
|
||||
const Agv = ({ lines, plane }: { lines: Types.RefLines; plane: Types.RefMesh; }) => {
|
||||
const pathPoints = useMemo(() => [
|
||||
[
|
||||
{ x: 8.477161935339709, y: 0, z: 17.41343083550102 },
|
||||
{ x: 9.175416491482693, y: 0, z: -12.361001232663693 },
|
||||
],
|
||||
// [
|
||||
// { x: 13.508213355232144, y: 0, z: -15.456970649652018 },
|
||||
// { x: -30.464866520869617, y: 0, z: 9.779806557688929 },
|
||||
// ],
|
||||
[
|
||||
{ x: 16.792040856420844, y: 0, z: 15.86281907549489 },
|
||||
{ x: -42.77173264503395, y: 0, z: -15.821322764400804 },
|
||||
],
|
||||
], []);
|
||||
const Agv = ({
|
||||
lines,
|
||||
plane,
|
||||
}: {
|
||||
lines: Types.RefLines;
|
||||
plane: Types.RefMesh;
|
||||
}) => {
|
||||
const [pathPoints, setPathPoints] = useState<
|
||||
{
|
||||
uuid: string;
|
||||
points: { x: number; y: number; z: number }[];
|
||||
}[]
|
||||
>([]);
|
||||
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;
|
||||
const [navMesh, setNavMesh] = useState();
|
||||
@@ -35,7 +96,12 @@ const Agv = ({ lines, plane }: { lines: Types.RefLines; plane: Types.RefMesh; })
|
||||
plane={plane}
|
||||
/>
|
||||
{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>
|
||||
</>
|
||||
|
||||
@@ -35,10 +35,14 @@ export default function NavMeshDetails({
|
||||
const [positions, indices] = getPositionsAndIndices(meshes);
|
||||
|
||||
const cs = 0.25;
|
||||
const ch = 0.5;
|
||||
const ch = 0.69;
|
||||
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) {
|
||||
return;
|
||||
@@ -49,7 +53,7 @@ export default function NavMeshDetails({
|
||||
const debugDrawer = new DebugDrawer();
|
||||
debugDrawer.drawNavMesh(navMesh);
|
||||
// scene.add(debugDrawer);
|
||||
} catch (error) { }
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
initializeNavigation();
|
||||
|
||||
@@ -1,98 +1,149 @@
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
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 { Line } from "@react-three/drei";
|
||||
import { useTh } from "leva/dist/declarations/src/styles";
|
||||
import { useActiveTool } from "../../../store/store";
|
||||
|
||||
// Define interface for props
|
||||
interface PathNavigatorProps {
|
||||
navMesh: any;
|
||||
selectedPoints: any;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export default function PathNavigator({
|
||||
navMesh,
|
||||
selectedPoints,
|
||||
id,
|
||||
}: PathNavigatorProps) {
|
||||
const [path, setPath] = useState<[number, number, number][]>([]);
|
||||
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);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedPoints.length === 2 && navMesh) {
|
||||
const [start, end] = selectedPoints;
|
||||
if (!start || !end) return;
|
||||
if (!scene || !id || path.length < 2) return;
|
||||
|
||||
const navMeshQuery = new NavMeshQuery(navMesh);
|
||||
|
||||
const { path: computedPath } = navMeshQuery.computePath(start, end);
|
||||
|
||||
if (computedPath.length > 0) {
|
||||
setPath(computedPath.map(({ x, y, z }) => [x, y + 0.1, z]));
|
||||
progressRef.current = 0;
|
||||
}
|
||||
let totalDistance = 0;
|
||||
const distances: number[] = [];
|
||||
for (let i = 0; i < path.length - 1; i++) {
|
||||
const start = new THREE.Vector3(...path[i]);
|
||||
const end = new THREE.Vector3(...path[i + 1]);
|
||||
const segmentDistance = start.distanceTo(end);
|
||||
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) => {
|
||||
if (path.length > 1 && meshRef.current) {
|
||||
const speed = 3;
|
||||
progressRef.current += delta * speed;
|
||||
if (!scene || !id || path.length < 2) return;
|
||||
|
||||
let totalDistance = 0;
|
||||
const distances: number[] = [];
|
||||
for (let i = 0; i < path.length - 1; i++) {
|
||||
const start = new THREE.Vector3(...path[i]);
|
||||
const end = new THREE.Vector3(...path[i + 1]);
|
||||
const segmentDistance = start.distanceTo(end);
|
||||
distances.push(segmentDistance);
|
||||
totalDistance += segmentDistance;
|
||||
}
|
||||
// Find the object in the scene
|
||||
const findObject = scene.getObjectByProperty("uuid", id);
|
||||
if (activeTool === "play") {
|
||||
if (!findObject) return;
|
||||
|
||||
const speed = 5;
|
||||
progressRef.current += delta * speed;
|
||||
|
||||
let coveredDistance = progressRef.current;
|
||||
let accumulatedDistance = 0;
|
||||
let index = 0;
|
||||
|
||||
// Determine the current segment of the path
|
||||
while (
|
||||
index < distances.length &&
|
||||
coveredDistance > accumulatedDistance + distances[index]
|
||||
index < distancesRef.current.length &&
|
||||
coveredDistance > accumulatedDistance + distancesRef.current[index]
|
||||
) {
|
||||
accumulatedDistance += distances[index];
|
||||
accumulatedDistance += distancesRef.current[index];
|
||||
index++;
|
||||
}
|
||||
|
||||
if (index < distances.length) {
|
||||
const start = new THREE.Vector3(...path[index]);
|
||||
const end = new THREE.Vector3(...path[index + 1]);
|
||||
const segmentDistance = distances[index];
|
||||
|
||||
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;
|
||||
// If the object has reached the end of the path, stop moving
|
||||
if (index >= distancesRef.current.length) {
|
||||
progressRef.current = totalDistanceRef.current;
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 (
|
||||
<>
|
||||
{/* {path.length > 0 && <Line points={path} color="blue" lineWidth={3} />} */}
|
||||
{path.length > 0 && (
|
||||
{path.length > 0 && <Line points={path} color="blue" lineWidth={3} />}
|
||||
{/* {path.length > 0 && (
|
||||
<mesh ref={meshRef} position={path.length > 0 ? path[0] : [0, 0.1, 0]}>
|
||||
<boxGeometry args={[1, 1, 1]} />
|
||||
<meshNormalMaterial />
|
||||
</mesh>
|
||||
)}
|
||||
)} */}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,26 @@
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteModels, useFloorItems, useLoadingProgress, useRenderDistance, useselectedFloorItem, useSelectedItem, useSocketStore, useToggleView, useTransformMode } from "../../../store/store";
|
||||
import {
|
||||
useActiveTool,
|
||||
useAsset3dWidget,
|
||||
useCamMode,
|
||||
useDeletableFloorItem,
|
||||
useDeleteModels,
|
||||
useFloorItems,
|
||||
useLoadingProgress,
|
||||
useRenderDistance,
|
||||
useselectedFloorItem,
|
||||
useSelectedItem,
|
||||
useSocketStore,
|
||||
useToggleView,
|
||||
useTransformMode,
|
||||
} from "../../../store/store";
|
||||
import assetVisibility from "../geomentries/assets/assetVisibility";
|
||||
import { useEffect } from "react";
|
||||
import * as THREE from "three";
|
||||
import * as Types from "../../../types/world/worldTypes";
|
||||
import assetManager, { cancelOngoingTasks } from "../geomentries/assets/assetManager";
|
||||
import assetManager, {
|
||||
cancelOngoingTasks,
|
||||
} from "../geomentries/assets/assetManager";
|
||||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
||||
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
|
||||
import DeletableHoveredFloorItems from "../geomentries/assets/deletableHoveredFloorItems";
|
||||
@@ -14,318 +30,405 @@ import addAssetModel from "../geomentries/assets/addAssetModel";
|
||||
import { getFloorAssets } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi";
|
||||
import useModuleStore from "../../../store/useModuleStore";
|
||||
// import { retrieveGLTF } from "../../../utils/indexDB/idbUtils";
|
||||
const assetManagerWorker = new Worker(new URL('../../../services/factoryBuilder/webWorkers/assetManagerWorker.js', import.meta.url));
|
||||
const gltfLoaderWorker = new Worker(new URL('../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js', import.meta.url));
|
||||
const assetManagerWorker = new Worker(
|
||||
new URL(
|
||||
"../../../services/factoryBuilder/webWorkers/assetManagerWorker.js",
|
||||
import.meta.url
|
||||
)
|
||||
);
|
||||
const gltfLoaderWorker = new Worker(
|
||||
new URL(
|
||||
"../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js",
|
||||
import.meta.url
|
||||
)
|
||||
);
|
||||
|
||||
const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject, floorGroup, tempLoader, isTempLoader, plane }: any) => {
|
||||
const state: Types.ThreeState = useThree();
|
||||
const { raycaster, controls }: any = state;
|
||||
const { renderDistance } = useRenderDistance();
|
||||
const { toggleView } = useToggleView();
|
||||
const { floorItems, setFloorItems } = useFloorItems();
|
||||
const { camMode } = useCamMode();
|
||||
const { deleteModels } = useDeleteModels();
|
||||
const { setDeletableFloorItem } = useDeletableFloorItem();
|
||||
const { transformMode } = useTransformMode();
|
||||
const { setselectedFloorItem } = useselectedFloorItem();
|
||||
const { activeTool } = useActiveTool();
|
||||
const { selectedItem, setSelectedItem } = useSelectedItem();
|
||||
const { setLoadingProgress } = useLoadingProgress();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { socket } = useSocketStore();
|
||||
const loader = new GLTFLoader();
|
||||
const dracoLoader = new DRACOLoader();
|
||||
const FloorItemsGroup = ({
|
||||
itemsGroup,
|
||||
hoveredDeletableFloorItem,
|
||||
AttachedObject,
|
||||
floorGroup,
|
||||
tempLoader,
|
||||
isTempLoader,
|
||||
plane,
|
||||
}: any) => {
|
||||
const state: Types.ThreeState = useThree();
|
||||
const { raycaster, controls }: any = state;
|
||||
const { renderDistance } = useRenderDistance();
|
||||
const { toggleView } = useToggleView();
|
||||
const { floorItems, setFloorItems } = useFloorItems();
|
||||
const { camMode } = useCamMode();
|
||||
const { deleteModels } = useDeleteModels();
|
||||
const { setDeletableFloorItem } = useDeletableFloorItem();
|
||||
const { transformMode } = useTransformMode();
|
||||
const { setselectedFloorItem } = useselectedFloorItem();
|
||||
const { activeTool } = useActiveTool();
|
||||
const { selectedItem, setSelectedItem } = useSelectedItem();
|
||||
const { setLoadingProgress } = useLoadingProgress();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { socket } = useSocketStore();
|
||||
const loader = new GLTFLoader();
|
||||
const dracoLoader = new DRACOLoader();
|
||||
|
||||
dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/');
|
||||
loader.setDRACOLoader(dracoLoader);
|
||||
dracoLoader.setDecoderPath(
|
||||
"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"
|
||||
);
|
||||
loader.setDRACOLoader(dracoLoader);
|
||||
|
||||
useEffect(() => {
|
||||
const email = localStorage.getItem('email');
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
useEffect(() => {
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email!.split("@")[1].split(".")[0];
|
||||
|
||||
let totalAssets = 0;
|
||||
let loadedAssets = 0;
|
||||
let totalAssets = 0;
|
||||
let loadedAssets = 0;
|
||||
|
||||
const updateLoadingProgress = (progress: number) => {
|
||||
if (progress < 100) {
|
||||
setLoadingProgress(progress);
|
||||
} else if (progress === 100) {
|
||||
setTimeout(() => {
|
||||
setLoadingProgress(100);
|
||||
setTimeout(() => {
|
||||
setLoadingProgress(0);
|
||||
}, 1500);
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
const updateLoadingProgress = (progress: number) => {
|
||||
if (progress < 100) {
|
||||
setLoadingProgress(progress);
|
||||
} else if (progress === 100) {
|
||||
setTimeout(() => {
|
||||
setLoadingProgress(100);
|
||||
setTimeout(() => {
|
||||
setLoadingProgress(0);
|
||||
}, 1500);
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
getFloorAssets(organization).then((data) => {
|
||||
const uniqueItems = (data as Types.FloorItems).filter((item, index, self) =>
|
||||
index === self.findIndex((t) => t.modelfileID === item.modelfileID)
|
||||
);
|
||||
totalAssets = uniqueItems.length;
|
||||
if (totalAssets === 0) {
|
||||
updateLoadingProgress(100);
|
||||
return;
|
||||
}
|
||||
gltfLoaderWorker.postMessage({ floorItems: data });
|
||||
getFloorAssets(organization).then((data) => {
|
||||
const uniqueItems = (data as Types.FloorItems).filter(
|
||||
(item, index, self) =>
|
||||
index === self.findIndex((t) => t.modelfileID === item.modelfileID)
|
||||
);
|
||||
totalAssets = uniqueItems.length;
|
||||
if (totalAssets === 0) {
|
||||
updateLoadingProgress(100);
|
||||
return;
|
||||
}
|
||||
gltfLoaderWorker.postMessage({ floorItems: data });
|
||||
});
|
||||
|
||||
gltfLoaderWorker.onmessage = async (event) => {
|
||||
if (event.data.message === "gltfLoaded" && event.data.modelBlob) {
|
||||
const blobUrl = URL.createObjectURL(event.data.modelBlob);
|
||||
|
||||
loader.load(blobUrl, (gltf) => {
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
THREE.Cache.remove(blobUrl);
|
||||
THREE.Cache.add(event.data.modelID, gltf);
|
||||
|
||||
loadedAssets++;
|
||||
const progress = Math.round((loadedAssets / totalAssets) * 100);
|
||||
updateLoadingProgress(progress);
|
||||
|
||||
if (loadedAssets === totalAssets) {
|
||||
loadInitialFloorItems(itemsGroup, setFloorItems);
|
||||
updateLoadingProgress(100);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
gltfLoaderWorker.onmessage = async (event) => {
|
||||
if (event.data.message === "gltfLoaded" && event.data.modelBlob) {
|
||||
const blobUrl = URL.createObjectURL(event.data.modelBlob);
|
||||
useEffect(() => {
|
||||
assetManagerWorker.onmessage = async (event) => {
|
||||
cancelOngoingTasks(); // Cancel the ongoing process
|
||||
await assetManager(event.data, itemsGroup, loader);
|
||||
};
|
||||
}, [assetManagerWorker]);
|
||||
|
||||
loader.load(blobUrl, (gltf) => {
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
THREE.Cache.remove(blobUrl);
|
||||
THREE.Cache.add(event.data.modelID, gltf);
|
||||
useEffect(() => {
|
||||
if (toggleView) return;
|
||||
|
||||
loadedAssets++;
|
||||
const progress = Math.round((loadedAssets / totalAssets) * 100);
|
||||
updateLoadingProgress(progress);
|
||||
const uuids: string[] = [];
|
||||
itemsGroup.current?.children.forEach((child: any) => {
|
||||
uuids.push(child.uuid);
|
||||
});
|
||||
const cameraPosition = state.camera.position;
|
||||
|
||||
if (loadedAssets === totalAssets) {
|
||||
loadInitialFloorItems(itemsGroup, setFloorItems);
|
||||
updateLoadingProgress(100);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
assetManagerWorker.postMessage({
|
||||
floorItems,
|
||||
cameraPosition,
|
||||
uuids,
|
||||
renderDistance,
|
||||
});
|
||||
}, [camMode, renderDistance]);
|
||||
|
||||
useEffect(() => {
|
||||
assetManagerWorker.onmessage = async (event) => {
|
||||
cancelOngoingTasks(); // Cancel the ongoing process
|
||||
await assetManager(event.data, itemsGroup, loader);
|
||||
};
|
||||
}, [assetManagerWorker]);
|
||||
useEffect(() => {
|
||||
const controls: any = state.controls;
|
||||
const camera: any = state.camera;
|
||||
|
||||
useEffect(() => {
|
||||
if (toggleView) return
|
||||
if (controls) {
|
||||
let intervalId: NodeJS.Timeout | null = null;
|
||||
|
||||
const handleChange = () => {
|
||||
if (toggleView) return;
|
||||
|
||||
const uuids: string[] = [];
|
||||
itemsGroup.current?.children.forEach((child: any) => {
|
||||
uuids.push(child.uuid);
|
||||
uuids.push(child.uuid);
|
||||
});
|
||||
const cameraPosition = state.camera.position;
|
||||
const cameraPosition = camera.position;
|
||||
|
||||
assetManagerWorker.postMessage({ floorItems, cameraPosition, uuids, renderDistance });
|
||||
}, [camMode, renderDistance]);
|
||||
assetManagerWorker.postMessage({
|
||||
floorItems,
|
||||
cameraPosition,
|
||||
uuids,
|
||||
renderDistance,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const controls: any = state.controls;
|
||||
const camera: any = state.camera;
|
||||
|
||||
if (controls) {
|
||||
let intervalId: NodeJS.Timeout | null = null;
|
||||
|
||||
const handleChange = () => {
|
||||
if (toggleView) return
|
||||
|
||||
const uuids: string[] = [];
|
||||
itemsGroup.current?.children.forEach((child: any) => {
|
||||
uuids.push(child.uuid);
|
||||
});
|
||||
const cameraPosition = camera.position;
|
||||
|
||||
assetManagerWorker.postMessage({ floorItems, cameraPosition, uuids, renderDistance });
|
||||
};
|
||||
|
||||
const startInterval = () => {
|
||||
if (!intervalId) {
|
||||
intervalId = setInterval(handleChange, 50);
|
||||
}
|
||||
};
|
||||
|
||||
const stopInterval = () => {
|
||||
handleChange();
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
intervalId = null;
|
||||
}
|
||||
};
|
||||
|
||||
controls.addEventListener('rest', handleChange);
|
||||
controls.addEventListener('rest', stopInterval);
|
||||
controls.addEventListener('control', startInterval);
|
||||
controls.addEventListener('controlend', stopInterval);
|
||||
|
||||
return () => {
|
||||
controls.removeEventListener('rest', handleChange);
|
||||
controls.removeEventListener('rest', stopInterval);
|
||||
controls.removeEventListener('control', startInterval);
|
||||
controls.removeEventListener('controlend', stopInterval);
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
};
|
||||
const startInterval = () => {
|
||||
if (!intervalId) {
|
||||
intervalId = setInterval(handleChange, 50);
|
||||
}
|
||||
}, [state.controls, floorItems, toggleView, renderDistance]);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = state.gl.domElement;
|
||||
let drag = false;
|
||||
let isLeftMouseDown = false;
|
||||
|
||||
const onMouseDown = (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = true;
|
||||
drag = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseMove = () => {
|
||||
if (isLeftMouseDown) {
|
||||
drag = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseUp = async (evt: any) => {
|
||||
if (controls) {
|
||||
(controls as any).enabled = true;
|
||||
}
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = false;
|
||||
if (drag) return;
|
||||
|
||||
if (deleteModels) {
|
||||
DeleteFloorItems(itemsGroup, hoveredDeletableFloorItem, setFloorItems, socket);
|
||||
}
|
||||
const Mode = transformMode;
|
||||
|
||||
if (Mode !== null || activeTool === "cursor") {
|
||||
if (!itemsGroup.current) return;
|
||||
let intersects = raycaster.intersectObjects(itemsGroup.current.children, true);
|
||||
if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
|
||||
// let currentObject = intersects[0].object;
|
||||
|
||||
// while (currentObject) {
|
||||
// if (currentObject.name === "Scene") {
|
||||
// break;
|
||||
// }
|
||||
// currentObject = currentObject.parent as THREE.Object3D;
|
||||
// }
|
||||
// if (currentObject) {
|
||||
// AttachedObject.current = currentObject as any;
|
||||
// setselectedFloorItem(AttachedObject.current!);
|
||||
// }
|
||||
} else {
|
||||
const target = controls.getTarget(new THREE.Vector3());
|
||||
await controls.setTarget(target.x, 0, target.z, true);
|
||||
setselectedFloorItem(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onDblClick = async (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = false;
|
||||
if (drag) return;
|
||||
|
||||
const Mode = transformMode;
|
||||
|
||||
if (Mode !== null || activeTool === "cursor") {
|
||||
if (!itemsGroup.current) return;
|
||||
let intersects = raycaster.intersectObjects(itemsGroup.current.children, true);
|
||||
if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
|
||||
let currentObject = intersects[0].object;
|
||||
|
||||
while (currentObject) {
|
||||
if (currentObject.name === "Scene") {
|
||||
break;
|
||||
}
|
||||
currentObject = currentObject.parent as THREE.Object3D;
|
||||
}
|
||||
if (currentObject) {
|
||||
AttachedObject.current = currentObject as any;
|
||||
// controls.fitToSphere(AttachedObject.current!, true);
|
||||
|
||||
const bbox = new THREE.Box3().setFromObject(AttachedObject.current);
|
||||
const size = bbox.getSize(new THREE.Vector3());
|
||||
const center = bbox.getCenter(new THREE.Vector3());
|
||||
|
||||
const front = new THREE.Vector3(0, 0, 1);
|
||||
AttachedObject.current.localToWorld(front);
|
||||
front.sub(AttachedObject.current.position).normalize();
|
||||
|
||||
const distance = Math.max(size.x, size.y, size.z) * 2;
|
||||
const newPosition = center.clone().addScaledVector(front, distance);
|
||||
|
||||
controls.setPosition(newPosition.x, newPosition.y, newPosition.z, true);
|
||||
controls.setTarget(center.x, center.y, center.z, true);
|
||||
controls.fitToBox(AttachedObject.current!, true, { cover: true, paddingTop: 5, paddingLeft: 5, paddingBottom: 5, paddingRight: 5 });
|
||||
|
||||
setselectedFloorItem(AttachedObject.current!);
|
||||
}
|
||||
} else {
|
||||
const target = controls.getTarget(new THREE.Vector3());
|
||||
await controls.setTarget(target.x, 0, target.z, true);
|
||||
setselectedFloorItem(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
const stopInterval = () => {
|
||||
handleChange();
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
intervalId = null;
|
||||
}
|
||||
};
|
||||
|
||||
const onDrop = (event: any) => {
|
||||
controls.addEventListener("rest", handleChange);
|
||||
controls.addEventListener("rest", stopInterval);
|
||||
controls.addEventListener("control", startInterval);
|
||||
controls.addEventListener("controlend", stopInterval);
|
||||
|
||||
if (!event.dataTransfer?.files[0]) return;
|
||||
|
||||
if (selectedItem.id !== "" && event.dataTransfer?.files[0]) {
|
||||
addAssetModel(raycaster, state.camera, state.pointer, floorGroup, setFloorItems, itemsGroup, isTempLoader, tempLoader, socket, selectedItem, setSelectedItem, plane);
|
||||
}
|
||||
return () => {
|
||||
controls.removeEventListener("rest", handleChange);
|
||||
controls.removeEventListener("rest", stopInterval);
|
||||
controls.removeEventListener("control", startInterval);
|
||||
controls.removeEventListener("controlend", stopInterval);
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
};
|
||||
}
|
||||
}, [state.controls, floorItems, toggleView, renderDistance]);
|
||||
|
||||
const onDragOver = (event: any) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
useEffect(() => {
|
||||
const canvasElement = state.gl.domElement;
|
||||
let drag = false;
|
||||
let isLeftMouseDown = false;
|
||||
|
||||
if (activeModule === "builder") {
|
||||
canvasElement.addEventListener("mousedown", onMouseDown);
|
||||
canvasElement.addEventListener("mouseup", onMouseUp);
|
||||
canvasElement.addEventListener("mousemove", onMouseMove);
|
||||
canvasElement.addEventListener("dblclick", onDblClick);
|
||||
canvasElement.addEventListener("drop", onDrop);
|
||||
canvasElement.addEventListener("dragover", onDragOver);
|
||||
} else {
|
||||
if (controls) {
|
||||
const target = controls.getTarget(new THREE.Vector3());
|
||||
controls.setTarget(target.x, 0, target.z, true);
|
||||
setselectedFloorItem(null);
|
||||
}
|
||||
}
|
||||
const onMouseDown = (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = true;
|
||||
drag = false;
|
||||
}
|
||||
};
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
canvasElement.removeEventListener("dblclick", onDblClick);
|
||||
canvasElement.removeEventListener("drop", onDrop);
|
||||
canvasElement.removeEventListener("dragover", onDragOver);
|
||||
};
|
||||
}, [deleteModels, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule]);
|
||||
const onMouseMove = () => {
|
||||
if (isLeftMouseDown) {
|
||||
drag = true;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('floorItems: ', floorItems);
|
||||
}, [floorItems])
|
||||
const onMouseUp = async (evt: any) => {
|
||||
if (controls) {
|
||||
(controls as any).enabled = true;
|
||||
}
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = false;
|
||||
if (drag) return;
|
||||
|
||||
useFrame(() => {
|
||||
if (controls)
|
||||
assetVisibility(itemsGroup, state.camera.position, renderDistance);
|
||||
if (deleteModels) {
|
||||
DeletableHoveredFloorItems(state, itemsGroup, hoveredDeletableFloorItem, setDeletableFloorItem);
|
||||
} else if (!deleteModels) {
|
||||
if (hoveredDeletableFloorItem.current) {
|
||||
hoveredDeletableFloorItem.current = undefined;
|
||||
setDeletableFloorItem(null);
|
||||
}
|
||||
DeleteFloorItems(
|
||||
itemsGroup,
|
||||
hoveredDeletableFloorItem,
|
||||
setFloorItems,
|
||||
socket
|
||||
);
|
||||
}
|
||||
})
|
||||
const Mode = transformMode;
|
||||
|
||||
return (
|
||||
<group ref={itemsGroup} name="itemsGroup">
|
||||
</group>
|
||||
)
|
||||
}
|
||||
if (Mode !== null || activeTool === "cursor") {
|
||||
if (!itemsGroup.current) return;
|
||||
let intersects = raycaster.intersectObjects(
|
||||
itemsGroup.current.children,
|
||||
true
|
||||
);
|
||||
if (
|
||||
intersects.length > 0 &&
|
||||
intersects[0]?.object?.parent?.parent?.position &&
|
||||
intersects[0]?.object?.parent?.parent?.scale &&
|
||||
intersects[0]?.object?.parent?.parent?.rotation
|
||||
) {
|
||||
// let currentObject = intersects[0].object;
|
||||
// while (currentObject) {
|
||||
// if (currentObject.name === "Scene") {
|
||||
// break;
|
||||
// }
|
||||
// currentObject = currentObject.parent as THREE.Object3D;
|
||||
// }
|
||||
// if (currentObject) {
|
||||
// AttachedObject.current = currentObject as any;
|
||||
// setselectedFloorItem(AttachedObject.current!);
|
||||
// }
|
||||
} else {
|
||||
const target = controls.getTarget(new THREE.Vector3());
|
||||
await controls.setTarget(target.x, 0, target.z, true);
|
||||
setselectedFloorItem(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default FloorItemsGroup;
|
||||
const onDblClick = async (evt: any) => {
|
||||
if (evt.button === 0) {
|
||||
isLeftMouseDown = false;
|
||||
if (drag) return;
|
||||
|
||||
const Mode = transformMode;
|
||||
|
||||
if (Mode !== null || activeTool === "cursor") {
|
||||
if (!itemsGroup.current) return;
|
||||
let intersects = raycaster.intersectObjects(
|
||||
itemsGroup.current.children,
|
||||
true
|
||||
);
|
||||
if (
|
||||
intersects.length > 0 &&
|
||||
intersects[0]?.object?.parent?.parent?.position &&
|
||||
intersects[0]?.object?.parent?.parent?.scale &&
|
||||
intersects[0]?.object?.parent?.parent?.rotation
|
||||
) {
|
||||
let currentObject = intersects[0].object;
|
||||
|
||||
while (currentObject) {
|
||||
if (currentObject.name === "Scene") {
|
||||
break;
|
||||
}
|
||||
currentObject = currentObject.parent as THREE.Object3D;
|
||||
}
|
||||
if (currentObject) {
|
||||
AttachedObject.current = currentObject as any;
|
||||
// controls.fitToSphere(AttachedObject.current!, true);
|
||||
|
||||
const bbox = new THREE.Box3().setFromObject(
|
||||
AttachedObject.current
|
||||
);
|
||||
const size = bbox.getSize(new THREE.Vector3());
|
||||
const center = bbox.getCenter(new THREE.Vector3());
|
||||
|
||||
const front = new THREE.Vector3(0, 0, 1);
|
||||
AttachedObject.current.localToWorld(front);
|
||||
front.sub(AttachedObject.current.position).normalize();
|
||||
|
||||
const distance = Math.max(size.x, size.y, size.z) * 2;
|
||||
const newPosition = center
|
||||
.clone()
|
||||
.addScaledVector(front, distance);
|
||||
|
||||
controls.setPosition(
|
||||
newPosition.x,
|
||||
newPosition.y,
|
||||
newPosition.z,
|
||||
true
|
||||
);
|
||||
controls.setTarget(center.x, center.y, center.z, true);
|
||||
controls.fitToBox(AttachedObject.current!, true, {
|
||||
cover: true,
|
||||
paddingTop: 5,
|
||||
paddingLeft: 5,
|
||||
paddingBottom: 5,
|
||||
paddingRight: 5,
|
||||
});
|
||||
|
||||
setselectedFloorItem(AttachedObject.current!);
|
||||
}
|
||||
} else {
|
||||
const target = controls.getTarget(new THREE.Vector3());
|
||||
await controls.setTarget(target.x, 0, target.z, true);
|
||||
setselectedFloorItem(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onDrop = (event: any) => {
|
||||
if (!event.dataTransfer?.files[0]) return;
|
||||
|
||||
if (selectedItem.id !== "" && event.dataTransfer?.files[0]) {
|
||||
addAssetModel(
|
||||
raycaster,
|
||||
state.camera,
|
||||
state.pointer,
|
||||
floorGroup,
|
||||
setFloorItems,
|
||||
itemsGroup,
|
||||
isTempLoader,
|
||||
tempLoader,
|
||||
socket,
|
||||
selectedItem,
|
||||
setSelectedItem,
|
||||
plane
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const onDragOver = (event: any) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
if (activeModule === "builder") {
|
||||
canvasElement.addEventListener("mousedown", onMouseDown);
|
||||
canvasElement.addEventListener("mouseup", onMouseUp);
|
||||
canvasElement.addEventListener("mousemove", onMouseMove);
|
||||
canvasElement.addEventListener("dblclick", onDblClick);
|
||||
canvasElement.addEventListener("drop", onDrop);
|
||||
canvasElement.addEventListener("dragover", onDragOver);
|
||||
} else {
|
||||
if (controls) {
|
||||
const target = controls.getTarget(new THREE.Vector3());
|
||||
controls.setTarget(target.x, 0, target.z, true);
|
||||
setselectedFloorItem(null);
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
canvasElement.removeEventListener("dblclick", onDblClick);
|
||||
canvasElement.removeEventListener("drop", onDrop);
|
||||
canvasElement.removeEventListener("dragover", onDragOver);
|
||||
};
|
||||
}, [
|
||||
deleteModels,
|
||||
transformMode,
|
||||
controls,
|
||||
selectedItem,
|
||||
state.camera,
|
||||
state.pointer,
|
||||
activeTool,
|
||||
activeModule,
|
||||
]);
|
||||
|
||||
useEffect(() => {}, [floorItems]);
|
||||
|
||||
useFrame(() => {
|
||||
if (controls)
|
||||
assetVisibility(itemsGroup, state.camera.position, renderDistance);
|
||||
if (deleteModels) {
|
||||
DeletableHoveredFloorItems(
|
||||
state,
|
||||
itemsGroup,
|
||||
hoveredDeletableFloorItem,
|
||||
setDeletableFloorItem
|
||||
);
|
||||
} else if (!deleteModels) {
|
||||
if (hoveredDeletableFloorItem.current) {
|
||||
hoveredDeletableFloorItem.current = undefined;
|
||||
setDeletableFloorItem(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return <group ref={itemsGroup} name="itemsGroup"></group>;
|
||||
};
|
||||
|
||||
export default FloorItemsGroup;
|
||||
|
||||
@@ -57,7 +57,6 @@ const FilterSearch: React.FC<ModelsProps> = ({
|
||||
const filteredModel = filteredModels?.filter((model) =>
|
||||
model.filename.toLowerCase().includes(val.toLowerCase())
|
||||
);
|
||||
|
||||
setModels(filteredModel);
|
||||
};
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ const MarketPlace = () => {
|
||||
const filteredAssets = async () => {
|
||||
try {
|
||||
const filt = await getAssetImages("67d934ad0f42a1fdadb19aa6");
|
||||
|
||||
setModels(filt.items);
|
||||
setFilteredModels(filt.items);
|
||||
} catch {}
|
||||
|
||||
@@ -1,22 +1,52 @@
|
||||
import { useToggleView } from '../../../store/store';
|
||||
import * as CONSTANTS from '../../../types/world/worldConstants';
|
||||
import { useTileDistance, useToggleView } from "../../../store/store";
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
|
||||
const Ground = ({ grid, plane }: any) => {
|
||||
const { toggleView } = useToggleView();
|
||||
const savedTheme: string | null = localStorage.getItem('theme');
|
||||
const { toggleView } = useToggleView();
|
||||
const savedTheme: string | null = localStorage.getItem("theme");
|
||||
const { planeValue, gridValue } = useTileDistance();
|
||||
|
||||
return (
|
||||
|
||||
<mesh name="Ground">
|
||||
<mesh ref={grid} name="Grid" position={!toggleView ? CONSTANTS.gridConfig.position3D : CONSTANTS.gridConfig.position2D}>
|
||||
<gridHelper args={[CONSTANTS.gridConfig.size, CONSTANTS.gridConfig.divisions, CONSTANTS.gridConfig.primaryColor, CONSTANTS.gridConfig.secondaryColor]} />
|
||||
</mesh>
|
||||
<mesh ref={plane} rotation-x={CONSTANTS.planeConfig.rotation} position={!toggleView ? CONSTANTS.planeConfig.position3D : CONSTANTS.planeConfig.position2D} name="Plane" receiveShadow>
|
||||
<planeGeometry args={[CONSTANTS.planeConfig.width, CONSTANTS.planeConfig.height]} />
|
||||
<meshBasicMaterial color={CONSTANTS.planeConfig.color} />
|
||||
</mesh>
|
||||
</mesh>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<mesh name="Ground">
|
||||
<mesh
|
||||
ref={grid}
|
||||
name="Grid"
|
||||
position={
|
||||
!toggleView
|
||||
? CONSTANTS.gridConfig.position3D
|
||||
: CONSTANTS.gridConfig.position2D
|
||||
}
|
||||
>
|
||||
<gridHelper
|
||||
args={[
|
||||
gridValue.size,
|
||||
gridValue.divisions,
|
||||
// CONSTANTS.gridConfig.size,
|
||||
// CONSTANTS.gridConfig.divisions,
|
||||
CONSTANTS.gridConfig.primaryColor,
|
||||
CONSTANTS.gridConfig.secondaryColor,
|
||||
]}
|
||||
/>
|
||||
</mesh>
|
||||
<mesh
|
||||
ref={plane}
|
||||
rotation-x={CONSTANTS.planeConfig.rotation}
|
||||
position={
|
||||
!toggleView
|
||||
? CONSTANTS.planeConfig.position3D
|
||||
: CONSTANTS.planeConfig.position2D
|
||||
}
|
||||
name="Plane"
|
||||
receiveShadow
|
||||
>
|
||||
<planeGeometry
|
||||
args={[planeValue.width, planeValue.height]}
|
||||
// args={[CONSTANTS.planeConfig.width, CONSTANTS.planeConfig.height]}
|
||||
/>
|
||||
<meshBasicMaterial color={CONSTANTS.planeConfig.color} />
|
||||
</mesh>
|
||||
</mesh>
|
||||
);
|
||||
};
|
||||
|
||||
export default Ground;
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
import { useRef, useEffect} from 'react';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import * as THREE from 'three';
|
||||
import { useAzimuth, useElevation, useShadows, useSunPosition, useFloorItems, useWallItems } from '../../../store/store';
|
||||
import * as CONSTANTS from '../../../types/world/worldConstants';
|
||||
const shadowWorker = new Worker(new URL('../../../services/factoryBuilder/webWorkers/shadowWorker', import.meta.url));
|
||||
import { useRef, useEffect } from "react";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import * as THREE from "three";
|
||||
import {
|
||||
useAzimuth,
|
||||
useElevation,
|
||||
useShadows,
|
||||
useSunPosition,
|
||||
useFloorItems,
|
||||
useWallItems,
|
||||
useTileDistance,
|
||||
} from "../../../store/store";
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
const shadowWorker = new Worker(
|
||||
new URL(
|
||||
"../../../services/factoryBuilder/webWorkers/shadowWorker",
|
||||
import.meta.url
|
||||
)
|
||||
);
|
||||
|
||||
export default function Shadows() {
|
||||
const { shadows, setShadows } = useShadows();
|
||||
@@ -15,6 +28,7 @@ export default function Shadows() {
|
||||
const { azimuth, setAzimuth } = useAzimuth();
|
||||
const { floorItems } = useFloorItems();
|
||||
const { wallItems } = useWallItems();
|
||||
const { planeValue } = useTileDistance();
|
||||
|
||||
useEffect(() => {
|
||||
gl.shadowMap.enabled = true;
|
||||
@@ -48,9 +62,9 @@ export default function Shadows() {
|
||||
useEffect(() => {
|
||||
if (controls && shadows) {
|
||||
updateShadows();
|
||||
(controls as any).addEventListener('update', updateShadows);
|
||||
(controls as any).addEventListener("update", updateShadows);
|
||||
return () => {
|
||||
(controls as any).removeEventListener('update', updateShadows);
|
||||
(controls as any).removeEventListener("update", updateShadows);
|
||||
};
|
||||
}
|
||||
}, [controls, elevation, azimuth, shadows]);
|
||||
@@ -75,10 +89,24 @@ export default function Shadows() {
|
||||
shadow-normalBias={CONSTANTS.shadowConfig.shadownormalBias}
|
||||
/>
|
||||
<object3D ref={targetRef} />
|
||||
<mesh position={CONSTANTS.shadowConfig.shadowMaterialPosition} rotation={CONSTANTS.shadowConfig.shadowMaterialRotation} receiveShadow>
|
||||
<planeGeometry args={[CONSTANTS.planeConfig.width, CONSTANTS.planeConfig.height]} />
|
||||
<shadowMaterial opacity={CONSTANTS.shadowConfig.shadowMaterialOpacity} transparent />
|
||||
<mesh
|
||||
position={CONSTANTS.shadowConfig.shadowMaterialPosition}
|
||||
rotation={CONSTANTS.shadowConfig.shadowMaterialRotation}
|
||||
receiveShadow
|
||||
>
|
||||
{/* <planeGeometry
|
||||
args={[CONSTANTS.planeConfig.width, CONSTANTS.planeConfig.height]}
|
||||
/>
|
||||
<shadowMaterial
|
||||
opacity={CONSTANTS.shadowConfig.shadowMaterialOpacity}
|
||||
transparent
|
||||
/> */}
|
||||
<planeGeometry args={[planeValue.width, planeValue.height]} />
|
||||
<shadowMaterial
|
||||
opacity={CONSTANTS.shadowConfig.shadowMaterialOpacity}
|
||||
transparent
|
||||
/>
|
||||
</mesh>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import {
|
||||
useWalls,
|
||||
useToolMode,
|
||||
useRefTextUpdate,
|
||||
useRenderDistance,
|
||||
useLimitDistance,
|
||||
} from "../../../store/store";
|
||||
|
||||
////////// 3D Function Imports //////////
|
||||
@@ -52,6 +54,7 @@ import Layer2DVisibility from "../../builder/geomentries/layers/layer2DVisibilit
|
||||
import DrieHtmlTemp from "../mqttTemp/drieHtmlTemp";
|
||||
import ZoneGroup from "../../builder/groups/zoneGroup";
|
||||
import Agv from "../../builder/agv/agv";
|
||||
import useModuleStore from "../../../store/useModuleStore";
|
||||
|
||||
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.
|
||||
@@ -117,9 +120,12 @@ export default function World() {
|
||||
const { roofVisibility, setRoofVisibility } = useRoofVisibility();
|
||||
const { wallVisibility, setWallVisibility } = useWallVisibility();
|
||||
const { shadows, setShadows } = useShadows();
|
||||
const { renderDistance, setRenderDistance } = useRenderDistance();
|
||||
const { limitDistance, setLimitDistance } = useLimitDistance();
|
||||
const { updateScene, setUpdateScene } = useUpdateScene();
|
||||
const { walls, setWalls } = useWalls();
|
||||
const { refTextupdate, setRefTextUpdate } = useRefTextUpdate();
|
||||
const { activeModule } = useModuleStore();
|
||||
|
||||
// const loader = new GLTFLoader();
|
||||
// const dracoLoader = new DRACOLoader();
|
||||
@@ -200,6 +206,8 @@ export default function World() {
|
||||
setRoofVisibility(visibility.roofVisibility);
|
||||
setWallVisibility(visibility.wallVisibility);
|
||||
setShadows(visibility.shadowVisibility);
|
||||
setRenderDistance(visibility.renderDistance);
|
||||
setLimitDistance(visibility.limitDistance);
|
||||
}
|
||||
}
|
||||
fetchVisibility();
|
||||
@@ -359,8 +367,7 @@ export default function World() {
|
||||
/>
|
||||
|
||||
{/* <DrieHtmlTemp itemsGroup={itemsGroup} /> */}
|
||||
|
||||
<Agv lines={lines} plane={plane} />
|
||||
{activeModule === "simulation" && <Agv lines={lines} plane={plane} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,299 +1,362 @@
|
||||
import * as THREE from 'three';
|
||||
import * as Types from '../../../types/world/worldTypes';
|
||||
import { useRef, useState, useEffect, useMemo } from 'react';
|
||||
import { Sphere, TransformControls } from '@react-three/drei';
|
||||
import { useEditingPoint, useEyeDropMode, useIsConnecting, usePreviewPosition, useRenderDistance, useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../../store/store';
|
||||
import { useFrame, useThree } from '@react-three/fiber';
|
||||
import { useSubModuleStore } from '../../../store/useModuleStore';
|
||||
import * as THREE from "three";
|
||||
import * as Types from "../../../types/world/worldTypes";
|
||||
import { useRef, useState, useEffect, useMemo } from "react";
|
||||
import { Sphere, TransformControls } from "@react-three/drei";
|
||||
import {
|
||||
useEditingPoint,
|
||||
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> }) {
|
||||
const { renderDistance } = useRenderDistance();
|
||||
const { setSubModule } = useSubModuleStore();
|
||||
const { setSelectedActionSphere, selectedActionSphere } = useSelectedActionSphere();
|
||||
const { eyeDropMode, setEyeDropMode } = useEyeDropMode();
|
||||
const { editingPoint, setEditingPoint } = useEditingPoint();
|
||||
const { previewPosition, setPreviewPosition } = usePreviewPosition();
|
||||
const { raycaster, camera, pointer, gl } = useThree();
|
||||
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
||||
const { setSelectedPath } = useSelectedPath();
|
||||
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
|
||||
const { isConnecting } = useIsConnecting();
|
||||
|
||||
const groupRefs = useRef<{ [key: string]: THREE.Group }>({});
|
||||
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
|
||||
const isMovingRef = useRef(false);
|
||||
const transformRef = useRef<any>(null);
|
||||
const [transformMode, setTransformMode] = useState<'translate' | 'rotate' | null>(null);
|
||||
function PathCreation({
|
||||
pathsGroupRef,
|
||||
}: {
|
||||
pathsGroupRef: React.MutableRefObject<THREE.Group>;
|
||||
}) {
|
||||
const { renderDistance } = useRenderDistance();
|
||||
const { setSubModule } = useSubModuleStore();
|
||||
const { setSelectedActionSphere, selectedActionSphere } =
|
||||
useSelectedActionSphere();
|
||||
const { eyeDropMode, setEyeDropMode } = useEyeDropMode();
|
||||
const { editingPoint, setEditingPoint } = useEditingPoint();
|
||||
const { previewPosition, setPreviewPosition } = usePreviewPosition();
|
||||
const { raycaster, camera, pointer, gl } = useThree();
|
||||
const plane = useMemo(
|
||||
() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0),
|
||||
[]
|
||||
);
|
||||
const { setSelectedPath } = useSelectedPath();
|
||||
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
|
||||
const { isConnecting } = useIsConnecting();
|
||||
|
||||
useEffect(() => {
|
||||
setTransformMode(null);
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (!selectedActionSphere) return;
|
||||
if (e.key === 'g') {
|
||||
setTransformMode(prev => prev === 'translate' ? null : 'translate');
|
||||
}
|
||||
if (e.key === 'r') {
|
||||
setTransformMode(prev => prev === 'rotate' ? null : 'rotate');
|
||||
}
|
||||
};
|
||||
const groupRefs = useRef<{ [key: string]: THREE.Group }>({});
|
||||
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
|
||||
const isMovingRef = useRef(false);
|
||||
const transformRef = useRef<any>(null);
|
||||
const [transformMode, setTransformMode] = useState<
|
||||
"translate" | "rotate" | null
|
||||
>(null);
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, [selectedActionSphere]);
|
||||
|
||||
useFrame(() => {
|
||||
Object.values(groupRefs.current).forEach(group => {
|
||||
if (group) {
|
||||
const distance = new THREE.Vector3(...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);
|
||||
useEffect(() => {
|
||||
setTransformMode(null);
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (!selectedActionSphere) return;
|
||||
if (e.key === "g") {
|
||||
setTransformMode((prev) => (prev === "translate" ? null : "translate"));
|
||||
}
|
||||
if (e.key === "r") {
|
||||
setTransformMode((prev) => (prev === "rotate" ? null : "rotate"));
|
||||
}
|
||||
};
|
||||
|
||||
useFrame(() => {
|
||||
if (eyeDropMode) {
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||
}, [selectedActionSphere]);
|
||||
|
||||
if (point) {
|
||||
setPreviewPosition({ x: point.x, y: point.z });
|
||||
}
|
||||
} else {
|
||||
setPreviewPosition(null);
|
||||
useFrame(() => {
|
||||
Object.values(groupRefs.current).forEach((group) => {
|
||||
if (group) {
|
||||
const distance = new THREE.Vector3(
|
||||
...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(() => {
|
||||
if (!camera) return;
|
||||
const canvasElement = gl.domElement;
|
||||
canvasElement.tabIndex = 0;
|
||||
setSimulationPaths(updatedPaths);
|
||||
};
|
||||
|
||||
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 = () => {
|
||||
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 (
|
||||
<group
|
||||
name={`${path.modeluuid}-event-path`}
|
||||
key={path.modeluuid}
|
||||
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
|
||||
position={path.position}
|
||||
rotation={path.rotation}
|
||||
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");
|
||||
}}
|
||||
>
|
||||
{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"
|
||||
}
|
||||
};
|
||||
}
|
||||
return path;
|
||||
});
|
||||
/>
|
||||
</Sphere>
|
||||
))}
|
||||
|
||||
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 (
|
||||
<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));
|
||||
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;
|
||||
})}
|
||||
|
||||
return (
|
||||
<group
|
||||
name={`${path.modeluuid}-event-path`}
|
||||
key={path.modeluuid}
|
||||
ref={el => (groupRefs.current[path.modeluuid] = el!)}
|
||||
position={path.position}
|
||||
rotation={path.rotation}
|
||||
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');
|
||||
}}
|
||||
>
|
||||
{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>
|
||||
);
|
||||
{selectedActionSphere && transformMode && (
|
||||
<TransformControls
|
||||
ref={transformRef}
|
||||
object={selectedActionSphere.point}
|
||||
mode={transformMode}
|
||||
onMouseUp={updateSimulationPaths}
|
||||
/>
|
||||
)}
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
export default PathCreation;
|
||||
|
||||
@@ -10,6 +10,7 @@ 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() {
|
||||
const { activeModule } = useModuleStore();
|
||||
@@ -41,6 +42,7 @@ function Simulation() {
|
||||
<PathCreation pathsGroupRef={pathsGroupRef} />
|
||||
<PathConnector pathsGroupRef={pathsGroupRef} />
|
||||
<ProcessContainer />
|
||||
{/* <Agv /> */}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { saveTemplateApi } from "../../services/realTimeVisulization/zoneData/saveTempleteApi";
|
||||
import { useSocketStore } from "../../store/store";
|
||||
import { Template } from "../../store/useTemplateStore";
|
||||
import { captureVisualization } from "./captureVisualization";
|
||||
|
||||
@@ -11,6 +12,7 @@ type HandleSaveTemplateProps = {
|
||||
widgets: any[];
|
||||
};
|
||||
templates?: Template[];
|
||||
visualizationSocket: any;
|
||||
};
|
||||
|
||||
// Generate a unique ID
|
||||
@@ -24,6 +26,7 @@ export const handleSaveTemplate = async ({
|
||||
widgets3D,
|
||||
selectedZone,
|
||||
templates = [],
|
||||
visualizationSocket,
|
||||
}: HandleSaveTemplateProps): Promise<void> => {
|
||||
try {
|
||||
// Check if the selected zone has any widgets
|
||||
@@ -47,7 +50,7 @@ export const handleSaveTemplate = async ({
|
||||
|
||||
// Capture visualization snapshot
|
||||
const snapshot = await captureVisualization();
|
||||
console.log("snapshot: ", snapshot);
|
||||
|
||||
// if (!snapshot) {
|
||||
// return;
|
||||
// }
|
||||
@@ -72,18 +75,25 @@ export const handleSaveTemplate = async ({
|
||||
console.error("Organization could not be determined from email.");
|
||||
return;
|
||||
}
|
||||
let saveTemplate = {
|
||||
organization: organization,
|
||||
template: newTemplate,
|
||||
};
|
||||
if (visualizationSocket) {
|
||||
visualizationSocket.emit("v2:viz-template:add", saveTemplate);
|
||||
}
|
||||
|
||||
// Save the template
|
||||
try {
|
||||
const response = await saveTemplateApi(organization, newTemplate);
|
||||
console.log("Save API Response:", response);
|
||||
addTemplate(newTemplate);
|
||||
// const response = await saveTemplateApi(organization, newTemplate);
|
||||
// console.log("Save API Response:", response);
|
||||
|
||||
// Add template only if API call succeeds
|
||||
addTemplate(newTemplate);
|
||||
} catch (apiError) {
|
||||
console.error("Error saving template to API:", apiError);
|
||||
// console.error("Error saving template to API:", apiError);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in handleSaveTemplate:", error);
|
||||
// console.error("Error in handleSaveTemplate:", error);
|
||||
}
|
||||
};
|
||||
|
||||
194
app/src/modules/visualization/realTimeVizSocket.dev.tsx
Normal file
194
app/src/modules/visualization/realTimeVizSocket.dev.tsx
Normal file
@@ -0,0 +1,194 @@
|
||||
import { useEffect } from "react";
|
||||
import { useSocketStore } from "../../store/store";
|
||||
import { useSelectedZoneStore } from "../../store/useZoneStore";
|
||||
import { useDroppedObjectsStore } from "../../store/useDroppedObjectsStore";
|
||||
import { useZoneWidgetStore } from "../../store/useZone3DWidgetStore";
|
||||
import useTemplateStore from "../../store/useTemplateStore";
|
||||
|
||||
export default function SocketRealTimeViz() {
|
||||
const { visualizationSocket } = useSocketStore();
|
||||
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
|
||||
const deleteObject = useDroppedObjectsStore((state) => state.deleteObject);
|
||||
const duplicateObject = useDroppedObjectsStore((state) => state.duplicateObject);
|
||||
const updateObjectPosition = useDroppedObjectsStore((state) => state.updateObjectPosition);
|
||||
const { addWidget } = useZoneWidgetStore()
|
||||
const { templates, removeTemplate } = useTemplateStore();
|
||||
const { setTemplates } = useTemplateStore();
|
||||
|
||||
useEffect(() => {
|
||||
const email = localStorage.getItem("email") || "";
|
||||
const organization = email?.split("@")[1]?.split(".")[0];
|
||||
if (visualizationSocket) {
|
||||
//add panel response
|
||||
visualizationSocket.on("viz-panel:response:updates", (addPanel: any) => {
|
||||
console.log('addPanel: ', addPanel);
|
||||
if (addPanel.success) {
|
||||
let addPanelData = addPanel.data.data
|
||||
setSelectedZone(addPanelData)
|
||||
}
|
||||
})
|
||||
//delete panel response
|
||||
visualizationSocket.on("viz-panel:response:delete", (deletePanel: any) => {
|
||||
console.log('deletePanel: ', deletePanel);
|
||||
if (deletePanel.success) {
|
||||
let deletePanelData = deletePanel.data.data
|
||||
setSelectedZone(deletePanelData)
|
||||
}
|
||||
})
|
||||
// add 2dWidget response
|
||||
visualizationSocket.on("viz-widget:response:updates", (add2dWidget: any) => {
|
||||
console.log('add2dWidget: ', add2dWidget);
|
||||
|
||||
if (add2dWidget.success && add2dWidget.data) {
|
||||
setSelectedZone((prev) => {
|
||||
const isWidgetAlreadyAdded = prev.widgets.some(
|
||||
(widget) => widget.id === add2dWidget.data.widgetData.id
|
||||
);
|
||||
if (isWidgetAlreadyAdded) return prev; // Prevent duplicate addition
|
||||
return {
|
||||
...prev,
|
||||
zoneId: add2dWidget.data.zoneId,
|
||||
zoneName: add2dWidget.data.zoneName,
|
||||
widgets: [...prev.widgets, add2dWidget.data.widgetData], // Append new widget
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
//delete 2D Widget response
|
||||
visualizationSocket.on("viz-widget:response:delete", (deleteWidget: any) => {
|
||||
console.log('deleteWidget: ', deleteWidget);
|
||||
|
||||
if (deleteWidget?.success && deleteWidget.data) {
|
||||
setSelectedZone((prevZone: any) => ({
|
||||
...prevZone,
|
||||
zoneId: deleteWidget.data.zoneId,
|
||||
zoneName: deleteWidget.data.zoneName,
|
||||
widgets: deleteWidget.data.widgetDeleteDatas, // Replace with new widget list
|
||||
}));
|
||||
}
|
||||
});
|
||||
//add Floating Widget response
|
||||
visualizationSocket.on("viz-float:response:updates", (addFloatingWidget: any) => {
|
||||
console.log('addFloatingWidget: ', addFloatingWidget);
|
||||
|
||||
if (addFloatingWidget.success) {
|
||||
if (addFloatingWidget.success && addFloatingWidget.message === "FloatWidget created successfully") {
|
||||
const state = useDroppedObjectsStore.getState();
|
||||
const zone = state.zones[addFloatingWidget.data.zoneName];
|
||||
if (!zone) {
|
||||
state.setZone(addFloatingWidget.data.zoneName, addFloatingWidget.data.zoneId);
|
||||
}
|
||||
const existingObjects = zone ? zone.objects : [];
|
||||
const newWidget = addFloatingWidget.data.widget;
|
||||
// ✅ Check if the widget ID already exists before adding
|
||||
const isAlreadyAdded = existingObjects.some(obj => obj.id === newWidget.id);
|
||||
if (isAlreadyAdded) {
|
||||
|
||||
return; // Don't add the widget if it already exists
|
||||
}
|
||||
// Add widget only if it doesn't exist
|
||||
state.addObject(addFloatingWidget.data.zoneName, newWidget);
|
||||
}
|
||||
if (addFloatingWidget.message === "Widget updated successfully") {
|
||||
updateObjectPosition(addFloatingWidget.data.zoneName, addFloatingWidget.data.index, addFloatingWidget.data.position);
|
||||
}
|
||||
}
|
||||
});
|
||||
//duplicate Floating Widget response
|
||||
visualizationSocket.on("viz-float:response:addDuplicate", (duplicateFloatingWidget: any) => {
|
||||
console.log('duplicateFloatingWidget: ', duplicateFloatingWidget);
|
||||
|
||||
if (duplicateFloatingWidget.success && duplicateFloatingWidget.message === "duplicate FloatWidget created successfully") {
|
||||
useDroppedObjectsStore.setState((state) => {
|
||||
const zone = state.zones[duplicateFloatingWidget.data.zoneName];
|
||||
if (!zone) return state; // Zone doesn't exist, return state as is
|
||||
const existingObjects = zone.objects;
|
||||
const newWidget = duplicateFloatingWidget.data.widget;
|
||||
// ✅ Check if the object with the same ID already exists
|
||||
const isAlreadyAdded = existingObjects.some(obj => obj.id === newWidget.id);
|
||||
if (isAlreadyAdded) {
|
||||
|
||||
return state; // Don't update state if it's already there
|
||||
}
|
||||
return {
|
||||
zones: {
|
||||
...state.zones,
|
||||
[duplicateFloatingWidget.data.zoneName]: {
|
||||
...zone,
|
||||
objects: [...existingObjects, newWidget], // Append only if it's not a duplicate
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
//delete Floating Widget response
|
||||
visualizationSocket.on("viz-float:response:delete", (deleteFloatingWidget: any) => {
|
||||
console.log('deleteFloatingWidget: ', deleteFloatingWidget);
|
||||
|
||||
if (deleteFloatingWidget.success) {
|
||||
deleteObject(deleteFloatingWidget.data.zoneName, deleteFloatingWidget.data.floatWidgetID);
|
||||
}
|
||||
});
|
||||
//add 3D Widget response
|
||||
visualizationSocket.on("viz-widget3D:response:updates", (add3DWidget: any) => {
|
||||
console.log('add3DWidget: ', add3DWidget);
|
||||
|
||||
if (add3DWidget.success) {
|
||||
if (add3DWidget.message === "Widget created successfully") {
|
||||
addWidget(add3DWidget.data.zoneId, add3DWidget.data.widget);
|
||||
}
|
||||
}
|
||||
});
|
||||
// add Template response
|
||||
visualizationSocket.on("viz-template:response:add", (addingTemplate: any) => {
|
||||
console.log('addingTemplate: ', addingTemplate);
|
||||
|
||||
if (addingTemplate.success) {
|
||||
if (addingTemplate.message === "Template saved successfully") {
|
||||
setTemplates(addingTemplate.data);
|
||||
}
|
||||
}
|
||||
});
|
||||
//load Template response
|
||||
visualizationSocket.on("viz-template:response:addTemplateZone", (loadTemplate: any) => {
|
||||
console.log('loadTemplate: ', loadTemplate);
|
||||
|
||||
if (loadTemplate.success) {
|
||||
if (loadTemplate.message === "Template placed in Zone") {
|
||||
let template = loadTemplate.data.template
|
||||
setSelectedZone({
|
||||
panelOrder: template.panelOrder,
|
||||
activeSides: Array.from(new Set(template.panelOrder)), // No merging with previous `activeSides`
|
||||
widgets: template.widgets,
|
||||
});
|
||||
useDroppedObjectsStore.getState().setZone(template.zoneName, template.zoneId);
|
||||
|
||||
if (Array.isArray(template.floatingWidget)) {
|
||||
template.floatingWidget.forEach((val: any) => {
|
||||
useDroppedObjectsStore.getState().addObject(template.zoneName, val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
//delete Template response
|
||||
visualizationSocket.on("viz-template:response:delete", (deleteTemplate: any) => {
|
||||
console.log('deleteTemplate: ', deleteTemplate);
|
||||
|
||||
if (deleteTemplate.success) {
|
||||
if (deleteTemplate.message === 'Template deleted successfully') {
|
||||
removeTemplate(deleteTemplate.data);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [visualizationSocket])
|
||||
|
||||
return (
|
||||
<></>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user