Merge branch 'main' into simulation-agv

This commit is contained in:
2025-03-29 14:30:02 +05:30
61 changed files with 3690 additions and 1814 deletions

View File

@@ -34,15 +34,11 @@ export default function NavMeshDetails({
const [positions, indices] = getPositionsAndIndices(meshes);
const cs = 0.5;
const cs = 0.25;
const ch = 0.5;
const walkableRadius = 0.89;
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;
@@ -53,7 +49,7 @@ export default function NavMeshDetails({
const debugDrawer = new DebugDrawer();
debugDrawer.drawNavMesh(navMesh);
// scene.add(debugDrawer);
} catch (error) {}
} catch (error) { }
};
initializeNavigation();

View File

@@ -86,7 +86,7 @@ export default function PathNavigator({
return (
<>
{path.length > 0 && <Line points={path} color="blue" lineWidth={3} />}
{/* {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]} />

View File

@@ -43,66 +43,46 @@ export default function PolygonGenerator({
const lineFeatures = result?.map((line: any) =>
turf.lineString(line.map((p: any) => p?.position))
);
);
const polygons = turf.polygonize(turf.featureCollection(lineFeatures));
renderWallGeometry(wallPoints);
let union: any = [];
if (polygons.features.length > 1) {
polygons.features.forEach((feature) => {
if (feature.geometry.type === "Polygon") {
polygons.features.forEach((feature) => {
union.push(feature);
});
const shape = new THREE.Shape();
const coords = feature.geometry.coordinates[0];
if (union.length > 1) {
const unionResult = turf.union(turf.featureCollection(union));
shape.moveTo(coords[0][0], coords[0][1]);
if (unionResult?.geometry.type === "MultiPolygon") {
unionResult.geometry.coordinates.forEach((poly) => {
const coordinates = poly[0].map(([x, z]) => {
return new THREE.Vector3(x, 0, z);
});
renderBoxGeometry(coordinates);
});
} else if (unionResult?.geometry.type === "Polygon") {
const coordinates = unionResult.geometry.coordinates[0].map(
([x, z]) => {
return new THREE.Vector3(x, 0, z);
for (let i = 1; i < coords.length; i++) {
shape.lineTo(coords[i][0], coords[i][1]);
}
);
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);
shape.lineTo(coords[0][0], coords[0][1]);
const extrudeSettings = {
depth: 5,
bevelEnabled: false,
};
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
const material = new THREE.MeshBasicMaterial({ color: "blue", transparent: true, opacity: 0.5 });
const mesh = new THREE.Mesh(geometry, material);
mesh.rotateX(Math.PI / 2);
mesh.name = "agv-collider";
mesh.position.y = 5;
mesh.receiveShadow = true;
groupRef.current?.add(mesh);
}
);
// setRooms((prevRooms) => [...prevRooms, coordinates]);
});
}
}, [lines.current]);
const renderBoxGeometry = (coordinates: THREE.Vector3[]) => {
const minX = Math.min(...coordinates.map((p) => p.x));
const maxX = Math.max(...coordinates.map((p) => p.x));
const minZ = Math.min(...coordinates.map((p) => p.z));
const maxZ = Math.max(...coordinates.map((p) => p.z));
const width = maxX - minX;
const depth = maxZ - minZ;
const height = 3;
const geometry = new THREE.BoxGeometry(width, height, depth);
const material = new THREE.MeshBasicMaterial({
color: "#ff66cc",
visible: false,
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set((minX + maxX) / 2, height / 2, (minZ + maxZ) / 2);
groupRef.current?.add(mesh);
};
const renderWallGeometry = (walls: THREE.Vector3[][]) => {
walls.forEach((wall) => {
if (wall.length < 2) return;

View File

@@ -1,5 +1,5 @@
import { useFrame, useThree } from "@react-three/fiber";
import { useActiveTool, 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";
@@ -33,7 +33,6 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
const { setLoadingProgress } = useLoadingProgress();
const { activeModule } = useModuleStore();
const { socket } = useSocketStore();
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
@@ -306,6 +305,8 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
};
}, [deleteModels, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule]);
useFrame(() => {
if (controls)
assetVisibility(itemsGroup, state.camera.position, renderDistance);

View File

@@ -224,6 +224,7 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable
const onDrop = (event: any) => {
if (!event.dataTransfer?.files[0]) return
pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(pointer, camera);

View File

@@ -1,112 +1,169 @@
import * as THREE from 'three';
import { useEffect, useRef, useState } from 'react';
import { useFrame } from '@react-three/fiber';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import camModel from '../../assets/gltf-glb/camera face 2.gltf';
import getActiveUsersData from '../../services/factoryBuilder/collab/getActiveUsers';
import { useActiveUsers, useSocketStore } from '../../store/store';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import { useNavigate } from 'react-router-dom';
import { Text, Html } from '@react-three/drei';
import CollabUserIcon from './collabUserIcon';
import image from '../../assets/image/userImage.png';
import * as THREE from "three";
import { useEffect, useRef, useState } from "react";
import { useFrame } from "@react-three/fiber";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import camModel from "../../assets/gltf-glb/camera face 2.gltf";
import getActiveUsersData from "../../services/factoryBuilder/collab/getActiveUsers";
import { useActiveUsers, useSocketStore } from "../../store/store";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { useNavigate } from "react-router-dom";
import { Html } from "@react-three/drei";
import CollabUserIcon from "./collabUserIcon";
import { getAvatarColor } from "./users/functions/getAvatarColor";
const CamModelsGroup = () => {
let navigate = useNavigate();
const groupRef = useRef<THREE.Group>(null);
const email = localStorage.getItem('email');
const email = localStorage.getItem("email");
const { activeUsers, setActiveUsers } = useActiveUsers();
const { socket } = useSocketStore();
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
const [cams, setCams] = useState<any[]>([]);
const [models, setModels] = useState<Record<string, { targetPosition: THREE.Vector3; targetRotation: THREE.Euler }>>({});
const [models, setModels] = useState<
Record<
string,
{ targetPosition: THREE.Vector3; targetRotation: THREE.Euler }
>
>({});
dracoLoader.setDecoderPath('three/examples/jsm/libs/draco/gltf/');
dracoLoader.setDecoderPath("three/examples/jsm/libs/draco/gltf/");
loader.setDRACOLoader(dracoLoader);
useEffect(() => {
if (!email) {
navigate('/');
navigate("/");
}
if (!socket) return;
const organization = email!.split('@')[1].split('.')[0];
const organization = email!.split("@")[1].split(".")[0];
socket.on('userConnectRespones', (data: any) => {
socket.on("userConnectRespones", (data: any) => {
if (!groupRef.current) return;
if (data.data.userData.email === email) return
if (socket.id === data.socketId || organization !== data.organization) return;
if (data.data.userData.email === email) return;
if (socket.id === data.socketId || organization !== data.organization)
return;
const model = groupRef.current.getObjectByProperty('uuid', data.data.userData._id);
const model = groupRef.current.getObjectByProperty(
"uuid",
data.data.userData._id
);
if (model) {
groupRef.current.remove(model);
}
loader.load(camModel, (gltf) => {
const newModel = gltf.scene.clone();
newModel.uuid = data.data.userData._id;
newModel.position.set(data.data.position.x, data.data.position.y, data.data.position.z);
newModel.rotation.set(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z);
newModel.position.set(
data.data.position.x,
data.data.position.y,
data.data.position.z
);
newModel.rotation.set(
data.data.rotation.x,
data.data.rotation.y,
data.data.rotation.z
);
newModel.userData = data.data.userData;
setCams((prev) => [...prev, newModel]);
setActiveUsers([...activeUsers, data.data.userData]);
});
});
socket.on('userDisConnectRespones', (data: any) => {
socket.on("userDisConnectRespones", (data: any) => {
if (!groupRef.current) return;
if (socket.id === data.socketId || organization !== data.organization) return;
if (socket.id === data.socketId || organization !== data.organization)
return;
setCams((prev) => prev.filter((cam) => cam.uuid !== data.data.userData._id));
setActiveUsers(activeUsers.filter((user: any) => user._id !== data.data.userData._id));
setCams((prev) =>
prev.filter((cam) => cam.uuid !== data.data.userData._id)
);
setActiveUsers(
activeUsers.filter((user: any) => user._id !== data.data.userData._id)
);
});
socket.on('cameraUpdateResponse', (data: any) => {
if (!groupRef.current || socket.id === data.socketId || organization !== data.organization) return;
socket.on("cameraUpdateResponse", (data: any) => {
if (
!groupRef.current ||
socket.id === data.socketId ||
organization !== data.organization
)
return;
setModels((prev) => ({
...prev,
[data.data.userId]: {
targetPosition: new THREE.Vector3(data.data.position.x, data.data.position.y, data.data.position.z),
targetRotation: new THREE.Euler(data.data.rotation.x, data.data.rotation.y, data.data.rotation.z),
targetPosition: new THREE.Vector3(
data.data.position.x,
data.data.position.y,
data.data.position.z
),
targetRotation: new THREE.Euler(
data.data.rotation.x,
data.data.rotation.y,
data.data.rotation.z
),
},
}));
});
return () => {
socket.off('userConnectRespones');
socket.off('userDisConnectRespones');
socket.off('cameraUpdateResponse');
socket.off("userConnectRespones");
socket.off("userDisConnectRespones");
socket.off("cameraUpdateResponse");
};
}, [socket]);
}, [socket, activeUsers]);
useFrame(() => {
if (!groupRef.current) return;
Object.keys(models).forEach((uuid) => {
const model = groupRef.current!.getObjectByProperty('uuid', uuid);
const model = groupRef.current!.getObjectByProperty("uuid", uuid);
if (!model) return;
const { targetPosition, targetRotation } = models[uuid];
model.position.lerp(targetPosition, 0.1);
model.rotation.x = THREE.MathUtils.lerp(model.rotation.x, targetRotation.x, 0.1);
model.rotation.y = THREE.MathUtils.lerp(model.rotation.y, targetRotation.y, 0.1);
model.rotation.z = THREE.MathUtils.lerp(model.rotation.z, targetRotation.z, 0.1);
model.rotation.x = THREE.MathUtils.lerp(
model.rotation.x,
targetRotation.x,
0.1
);
model.rotation.y = THREE.MathUtils.lerp(
model.rotation.y,
targetRotation.y,
0.1
);
model.rotation.z = THREE.MathUtils.lerp(
model.rotation.z,
targetRotation.z,
0.1
);
});
});
useEffect(() => {
if (!groupRef.current) return;
const organization = email!.split('@')[1].split('.')[0];
const organization = email!.split("@")[1].split(".")[0];
getActiveUsersData(organization).then((data) => {
const filteredData = data.cameraDatas.filter((camera: any) => camera.userData.email !== email);
const filteredData = data.cameraDatas.filter(
(camera: any) => camera.userData.email !== email
);
if (filteredData.length > 0) {
loader.load(camModel, (gltf) => {
const newCams = filteredData.map((cam: any) => {
const newModel = gltf.scene.clone();
newModel.uuid = cam.userData._id;
newModel.position.set(cam.position.x, cam.position.y, cam.position.z);
newModel.rotation.set(cam.rotation.x, cam.rotation.y, cam.rotation.z);
newModel.position.set(
cam.position.x,
cam.position.y,
cam.position.z
);
newModel.rotation.set(
cam.rotation.x,
cam.rotation.y,
cam.rotation.z
);
newModel.userData = cam.userData;
console.log('cam.userData: ', cam.userData);
setActiveUsers([...activeUsers, cam.userData]);
return newModel;
});
@@ -119,7 +176,7 @@ const CamModelsGroup = () => {
return (
<group ref={groupRef} name="Cam-Model-Group">
{cams.map((cam, index) => (
<primitive key={index} object={cam} >
<primitive key={index} object={cam}>
<Html
as="div"
center
@@ -130,8 +187,14 @@ const CamModelsGroup = () => {
textAlign: "center",
fontFamily: "Arial, sans-serif",
}}
position={[-0.015, 0, 0.7]}>
<CollabUserIcon color={"#ff0000"} userImage={image} userName={cam.userData.userName} />
position={[-0.015, 0, 0.7]}
>
<CollabUserIcon
userImage={""}
userName={cam.userData.userName}
index={index}
color={getAvatarColor(index)}
/>
</Html>
</primitive>
))}

View File

@@ -1,53 +1,33 @@
import React from "react";
import CustomAvatar from "./users/Avatar";
interface CollabUserIconProps {
color: string;
userImage: string;
userName: string;
userName: string;
userImage?: string;
index?: number;
color: string;
}
const CollabUserIcon: React.FC<CollabUserIconProps> = ({
color,
userImage,
userName,
userImage,
userName,
index = 0,
color,
}) => {
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
gap: "6px",
// transform:"translate(-20%, 0%)",
}}
>
<img
src={userImage}
alt={userName}
style={{
width: "30px",
height: "30px",
outline: `2px solid ${color}`,
borderRadius: "50%",
objectFit: 'cover'
}}
/>
<div
style={{
display: 'flex',
padding: "3px 5px",
backgroundColor: color,
borderRadius: "6px",
color: "white",
fontSize: "14px",
fontWeight: 400
}}
>
{userName}
</div>
</div>
);
return (
<div className="collab-user-live-container">
<div className="user-image-container">
{userImage ? (
<img className="user-image" src={userImage} alt={userName} />
) : (
<CustomAvatar name={userName} index={index} />
)}
</div>
<div className="user-name" style={{ backgroundColor: color }}>
{userName}
</div>
</div>
);
};
export default CollabUserIcon;

View File

@@ -0,0 +1,59 @@
import React, { useEffect, useState } from "react";
import { getInitials } from "./functions/getInitials";
import { getAvatarColor } from "./functions/getAvatarColor";
interface AvatarProps {
name: string; // Name can be a full name or initials
size?: number;
index?: number;
textColor?: string;
}
const CustomAvatar: React.FC<AvatarProps> = ({
name,
size = 100,
index = 0,
textColor = "#ffffff",
}) => {
const [imageSrc, setImageSrc] = useState<string | null>(null);
useEffect(() => {
const canvas = document.createElement("canvas"); // Create an offscreen canvas
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext("2d");
if (ctx) {
const initials = getInitials(name); // Convert name to initials if needed
// Draw background
ctx.fillStyle = getAvatarColor(index);
ctx.fillRect(0, 0, size, size);
// Draw initials
ctx.fillStyle = textColor;
ctx.font = `bold ${size / 2}px Arial`;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(initials, size / 2, size / 2);
// Generate image source
const dataURL = canvas.toDataURL("image/png");
setImageSrc(dataURL);
}
}, [name, size, textColor]);
if (!imageSrc) {
return null; // Return null while the image is being generated
}
return (
<img
className="user-image"
src={imageSrc}
alt="User Avatar"
style={{ width: "100%", height: "100%" }}
/>
);
};
export default CustomAvatar;

View File

@@ -0,0 +1,26 @@
const avatarColors: string[] = [
"#FF5733", // Red Orange
"#48ac2a", // Leaf Green
"#0050eb", // Royal Blue
"#FF33A1", // Hot Pink
"#FF8C33", // Deep Orange
"#8C33FF", // Violet
"#FF3333", // Bright Red
"#43c06d", // Emerald Green
"#A133FF", // Amethyst Purple
"#C70039", // Crimson
"#900C3F", // Maroon
"#581845", // Plum
"#3498DB", // Sky Blue
"#2ECC71", // Green Mint
"#E74C3C", // Tomato Red
"#00adff", // Azure
"#DBAD05", // Amber Yellow
"#FF5733", // Red Orange
"#FF33A1", // Hot Pink
"#900C3F", // Maroon
];
export function getAvatarColor(index: number): string {
return avatarColors[index % avatarColors.length];
}

View File

@@ -0,0 +1,10 @@
export const getInitials = (fullName: string): string => {
// Extract initials from the name
const words = fullName.split(" ");
const initials = words
.map((word) => word[0])
.slice(0, 2)
.join("")
.toUpperCase();
return initials;
};

View File

@@ -29,11 +29,10 @@ const FilterSearch: React.FC<ModelsProps> = ({
filteredModels,
}) => {
const [activeOption, setActiveOption] = useState("Sort by"); // State for active option
console.log("filteredModels: ", filteredModels);
const handleSelect = (option: string) => {
setActiveOption(option);
console.log("option: ", option);
// Alphabet ascending
// Alphabet descending
// All

View File

@@ -19,6 +19,8 @@ import DroppedObjects from "../../components/ui/componets/DroppedFloatingWidgets
// import Simulation from "./simulationtemp/simulation";
import ZoneCentreTarget from "../../components/ui/componets/zoneCameraTarget";
import ProductionCapacity from "../../components/layout/3D-cards/cards/ProductionCapacity";
import Dropped3dWidgets from "../../components/ui/componets/Dropped3dWidget";
export default function Scene() {
@@ -43,13 +45,13 @@ export default function Scene() {
}}
>
<Dropped3dWidgets/>
<Controls />
<TransformControl />
<SelectionControls />
<MeasurementTool />
<World />
<ZoneCentreTarget />
{/* <Simulation /> */}
<Simulation />
<PostProcessing />
<Sun />
@@ -58,9 +60,6 @@ export default function Scene() {
<MqttEvents />
<Environment files={background} environmentIntensity={1.5} />
</Canvas>
</KeyboardControls>
);
}

View File

@@ -1,43 +1,29 @@
import { useFloorItems } from '../../../store/store';
import { useFloorItems, useSimulationPaths } from '../../../store/store';
import * as THREE from 'three';
import * as Types from '../../../types/world/worldTypes';
import { useEffect } from 'react';
interface Path {
modeluuid: string;
modelName: string;
points: {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
actions: { uuid: string; name: string; type: string; material: string; delay: number | string; spawnInterval: number | string; isUsed: boolean }[] | [];
triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] | [];
connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] };
}[];
pathPosition: [number, number, number];
pathRotation: [number, number, number];
speed: number;
}
function Behaviour({ setSimulationPaths }: { setSimulationPaths: any }) {
function Behaviour() {
const { setSimulationPaths } = useSimulationPaths();
const { floorItems } = useFloorItems();
useEffect(() => {
const newPaths: Path[] = [];
const newPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[] = [];
floorItems.forEach((item: Types.FloorItemType) => {
if (item.modelfileID === "6633215057b31fe671145959") {
const point1Position = new THREE.Vector3(0, 1.25, 3.3);
const middlePointPosition = new THREE.Vector3(0, 1.25, 0);
const point2Position = new THREE.Vector3(0, 1.25, -3.3);
if (item.modelfileID === "672a090f80d91ac979f4d0bd") {
const point1Position = new THREE.Vector3(0, 0.85, 2.2);
const middlePointPosition = new THREE.Vector3(0, 0.85, 0);
const point2Position = new THREE.Vector3(0, 0.85, -2.2);
const point1UUID = THREE.MathUtils.generateUUID();
const middlePointUUID = THREE.MathUtils.generateUUID();
const point2UUID = THREE.MathUtils.generateUUID();
const newPath: Path = {
const newPath: Types.ConveyorEventsSchema = {
modeluuid: item.modeluuid,
modelName: item.modelname,
type: 'Conveyor',
points: [
{
uuid: point1UUID,
@@ -64,12 +50,32 @@ function Behaviour({ setSimulationPaths }: { setSimulationPaths: any }) {
connections: { source: { pathUUID: item.modeluuid, pointUUID: point2UUID }, targets: [] },
},
],
pathPosition: [...item.position],
pathRotation: [item.rotation.x, item.rotation.y, item.rotation.z],
assetPosition: [...item.position],
assetRotation: [item.rotation.x, item.rotation.y, item.rotation.z],
speed: 1,
};
newPaths.push(newPath);
} else if (item.modelfileID === "67e3da19c2e8f37134526e6a") {
const pointUUID = THREE.MathUtils.generateUUID();
const pointPosition = new THREE.Vector3(0, 1.3, 0);
const newVehiclePath: Types.VehicleEventsSchema = {
modeluuid: item.modeluuid,
modelName: item.modelname,
type: 'Vehicle',
point: {
uuid: pointUUID,
position: [pointPosition.x, pointPosition.y, pointPosition.z],
actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Start', start: THREE.MathUtils.generateUUID(), hitCount: 1, end: THREE.MathUtils.generateUUID(), buffer: 0, isUsed: false }],
triggers: [],
connections: { source: { pathUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] },
},
assetPosition: [...item.position],
speed: 2,
};
newPaths.push(newVehiclePath);
}
});

View File

@@ -1,6 +1,7 @@
import { useFrame, useThree } from '@react-three/fiber';
import React, { useEffect, useState } from 'react';
import * as THREE from 'three';
import * as Types from '../../../types/world/worldTypes';
import { QuadraticBezierLine } from '@react-three/drei';
import { useIsConnecting, useSimulationPaths } from '../../../store/store';
import useModuleStore from '../../../store/useModuleStore';
@@ -27,61 +28,113 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
toPointUUID: string
) => {
const updatedPaths = simulationPaths.map(path => {
if (path.modeluuid === fromPathUUID) {
return {
...path,
points: path.points.map(point => {
if (point.uuid === fromPointUUID) {
const newTarget = {
pathUUID: toPathUUID,
pointUUID: toPointUUID
};
const existingTargets = point.connections.targets || [];
if (!existingTargets.some(target =>
target.pathUUID === newTarget.pathUUID &&
target.pointUUID === newTarget.pointUUID
)) {
return {
...point,
connections: {
...point.connections,
targets: [...existingTargets, newTarget]
}
if (path.type === 'Conveyor') {
if (path.modeluuid === fromPathUUID) {
return {
...path,
points: path.points.map(point => {
if (point.uuid === fromPointUUID) {
const newTarget = {
pathUUID: toPathUUID,
pointUUID: toPointUUID
};
const existingTargets = point.connections.targets || [];
if (!existingTargets.some(target =>
target.pathUUID === newTarget.pathUUID &&
target.pointUUID === newTarget.pointUUID
)) {
return {
...point,
connections: {
...point.connections,
targets: [...existingTargets, newTarget]
}
};
}
}
}
return point;
})
};
return point;
})
};
}
else if (path.modeluuid === toPathUUID) {
return {
...path,
points: path.points.map(point => {
if (point.uuid === toPointUUID) {
const reverseTarget = {
pathUUID: fromPathUUID,
pointUUID: fromPointUUID
};
const existingTargets = point.connections.targets || [];
if (!existingTargets.some(target =>
target.pathUUID === reverseTarget.pathUUID &&
target.pointUUID === reverseTarget.pointUUID
)) {
return {
...point,
connections: {
...point.connections,
targets: [...existingTargets, reverseTarget]
}
};
}
}
return point;
})
};
}
}
else if (path.modeluuid === toPathUUID) {
return {
...path,
points: path.points.map(point => {
if (point.uuid === toPointUUID) {
const reverseTarget = {
pathUUID: fromPathUUID,
pointUUID: fromPointUUID
};
const existingTargets = point.connections.targets || [];
else if (path.type === 'Vehicle') {
// Handle outgoing connections from Vehicle
if (path.modeluuid === fromPathUUID && path.point.uuid === fromPointUUID) {
const newTarget = {
pathUUID: toPathUUID,
pointUUID: toPointUUID
};
const existingTargets = path.point.connections.targets || [];
if (!existingTargets.some(target =>
target.pathUUID === reverseTarget.pathUUID &&
target.pointUUID === reverseTarget.pointUUID
)) {
return {
...point,
connections: {
...point.connections,
targets: [...existingTargets, reverseTarget]
}
};
if (!existingTargets.some(target =>
target.pathUUID === newTarget.pathUUID &&
target.pointUUID === newTarget.pointUUID
)) {
return {
...path,
point: {
...path.point,
connections: {
...path.point.connections,
targets: [...existingTargets, newTarget]
}
}
}
return point;
})
};
};
}
}
// Handle incoming connections to Vehicle
else if (path.modeluuid === toPathUUID && path.point.uuid === toPointUUID) {
const reverseTarget = {
pathUUID: fromPathUUID,
pointUUID: fromPointUUID
};
const existingTargets = path.point.connections.targets || [];
if (!existingTargets.some(target =>
target.pathUUID === reverseTarget.pathUUID &&
target.pointUUID === reverseTarget.pointUUID
)) {
return {
...path,
point: {
...path.point,
connections: {
...path.point.connections,
targets: [...existingTargets, reverseTarget]
}
}
};
}
}
}
return path;
});
@@ -126,25 +179,43 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
if (intersects.length > 0) {
const intersected = intersects[0].object;
if (intersected.name.includes("action-sphere")) {
if (intersected.name.includes("events-sphere")) {
const pathUUID = intersected.userData.path.modeluuid;
const sphereUUID = intersected.uuid;
const worldPosition = new THREE.Vector3();
intersected.getWorldPosition(worldPosition);
const isStartOrEnd = intersected.userData.path.points.length > 0 && (
sphereUUID === intersected.userData.path.points[0].uuid ||
sphereUUID === intersected.userData.path.points[intersected.userData.path.points.length - 1].uuid
);
let isStartOrEnd = false;
if (intersected.userData.path.points) {
isStartOrEnd = intersected.userData.path.points.length > 0 && (
sphereUUID === intersected.userData.path.points[0].uuid ||
sphereUUID === intersected.userData.path.points[intersected.userData.path.points.length - 1].uuid
);
} else if (intersected.userData.path.point) {
isStartOrEnd = sphereUUID === intersected.userData.path.point.uuid;
}
if (pathUUID) {
// Check if sphere is already connected
const isAlreadyConnected = simulationPaths.some(path =>
path.points.some(point =>
point.uuid === sphereUUID &&
point.connections.targets.length > 0
)
);
const firstPath = simulationPaths.find(p => p.modeluuid === firstSelected?.pathUUID);
const secondPath = simulationPaths.find(p => p.modeluuid === pathUUID);
if (firstPath && secondPath && firstPath.type === 'Vehicle' && secondPath.type === 'Vehicle') {
console.log("Cannot connect two vehicle paths together");
return;
}
const isAlreadyConnected = simulationPaths.some(path => {
if (path.type === 'Conveyor') {
return path.points.some(point =>
point.uuid === sphereUUID &&
point.connections.targets.length > 0
);
} else if (path.type === 'Vehicle') {
return path.point.uuid === sphereUUID &&
path.point.connections.targets.length > 0;
}
return false;
});
if (isAlreadyConnected) {
console.log("Sphere is already connected. Ignoring.");
@@ -211,6 +282,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
raycaster.setFromCamera(pointer, camera);
const intersects = raycaster.intersectObjects(scene.children, true).filter((intersect) =>
!intersect.object.name.includes("Roof") &&
!intersect.object.name.includes("agv-collider") &&
!intersect.object.name.includes("MeasurementReference") &&
!intersect.object.userData.isPathObject &&
!(intersect.object.type === "GridHelper")
@@ -229,7 +301,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
}
const sphereIntersects = raycaster.intersectObjects(pathsGroupRef.current.children, true).filter((obj) =>
obj.object.name.includes("action-sphere")
obj.object.name.includes("events-sphere")
);
if (sphereIntersects.length > 0) {
@@ -237,27 +309,45 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
const sphereUUID = sphere.uuid;
const spherePosition = new THREE.Vector3();
sphere.getWorldPosition(spherePosition);
const pathUUID = sphere.userData.path.modeluuid;
const pathData = sphere.userData.path;
const pathUUID = pathData.modeluuid;
const isStartOrEnd = sphere.userData.path.points.length > 0 && (
sphereUUID === sphere.userData.path.points[0].uuid ||
sphereUUID === sphere.userData.path.points[sphere.userData.path.points.length - 1].uuid
);
const firstPath = simulationPaths.find(p => p.modeluuid === firstSelected.pathUUID);
const secondPath = simulationPaths.find(p => p.modeluuid === pathUUID);
const isVehicleToVehicle = firstPath?.type === 'Vehicle' && secondPath?.type === 'Vehicle';
const isAlreadyConnected = simulationPaths.some(path =>
path.points.some(point =>
point.uuid === sphereUUID &&
point.connections.targets.length > 0
)
);
const isConnectable = (pathData.type === 'Vehicle' ||
(pathData.points.length > 0 && (
sphereUUID === pathData.points[0].uuid ||
sphereUUID === pathData.points[pathData.points.length - 1].uuid
))) && !isVehicleToVehicle;
const isAlreadyConnected = simulationPaths.some(path => {
if (path.type === 'Conveyor') {
return path.points.some(point =>
point.uuid === sphereUUID &&
point.connections.targets.length > 0
);
} else if (path.type === 'Vehicle') {
return path.point.uuid === sphereUUID &&
path.point.connections.targets.length > 0;
}
return false;
});
if (
!isAlreadyConnected &&
!isVehicleToVehicle &&
firstSelected.sphereUUID !== sphereUUID &&
firstSelected.pathUUID !== pathUUID &&
(firstSelected.isCorner || isStartOrEnd)
(firstSelected.isCorner || isConnectable)
) {
snappedSphere = { sphereUUID, position: spherePosition, pathUUID, isCorner: isStartOrEnd };
snappedSphere = {
sphereUUID,
position: spherePosition,
pathUUID,
isCorner: isConnectable
};
} else {
isInvalidConnection = true;
}
@@ -281,8 +371,13 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
end: point,
mid: midPoint,
});
console.log({
start: firstSelected.position,
end: point,
mid: midPoint,
});
setIsConnecting(true);
// setIsConnecting(true);
if (sphereIntersects.length > 0) {
setHelperLineColor(isInvalidConnection ? 'red' : '#6cf542');
@@ -299,13 +394,53 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
}
});
// Render connections from simulationPaths
return (
<>
{simulationPaths.flatMap(path =>
path.points.flatMap(point =>
point.connections.targets.map((target, index) => {
const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', point.uuid);
{simulationPaths.flatMap(path => {
if (path.type === 'Conveyor') {
return path.points.flatMap(point =>
point.connections.targets.map((target, index) => {
const targetPath = simulationPaths.find(p => p.modeluuid === target.pathUUID);
if (targetPath?.type === 'Vehicle') return null;
const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', point.uuid);
const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID);
if (fromSphere && toSphere) {
const fromWorldPosition = new THREE.Vector3();
const toWorldPosition = new THREE.Vector3();
fromSphere.getWorldPosition(fromWorldPosition);
toSphere.getWorldPosition(toWorldPosition);
const distance = fromWorldPosition.distanceTo(toWorldPosition);
const heightFactor = Math.max(0.5, distance * 0.2);
const midPoint = new THREE.Vector3(
(fromWorldPosition.x + toWorldPosition.x) / 2,
Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor,
(fromWorldPosition.z + toWorldPosition.z) / 2
);
return (
<QuadraticBezierLine
key={`${point.uuid}-${target.pointUUID}-${index}`}
start={fromWorldPosition.toArray()}
end={toWorldPosition.toArray()}
mid={midPoint.toArray()}
color="white"
lineWidth={4}
dashed
dashSize={0.75}
dashScale={20}
/>
);
}
return null;
})
);
} else if (path.type === 'Vehicle') {
return path.point.connections.targets.map((target, index) => {
const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', path.point.uuid);
const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID);
if (fromSphere && toSphere) {
@@ -325,22 +460,23 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
return (
<QuadraticBezierLine
key={`${point.uuid}-${target.pointUUID}-${index}`}
key={`${path.point.uuid}-${target.pointUUID}-${index}`}
start={fromWorldPosition.toArray()}
end={toWorldPosition.toArray()}
mid={midPoint.toArray()}
color="white"
color="orange"
lineWidth={4}
dashed
dashSize={1}
dashSize={0.75}
dashScale={20}
/>
);
}
return null;
})
)
)}
});
}
return [];
})}
{currentLine && (
<QuadraticBezierLine

View File

@@ -4,10 +4,12 @@ import { Sphere, TransformControls } from '@react-three/drei';
import { useIsConnecting, useRenderDistance, useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../../store/store';
import { useFrame, useThree } from '@react-three/fiber';
import { useSubModuleStore } from '../../../store/useModuleStore';
import { point } from '@turf/helpers';
interface Path {
interface ConveyorEventsSchema {
modeluuid: string;
modelName: string;
type: 'Conveyor';
points: {
uuid: string;
position: [number, number, number];
@@ -16,8 +18,8 @@ interface Path {
triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] | [];
connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] };
}[];
pathPosition: [number, number, number];
pathRotation: [number, number, number];
assetPosition: [number, number, number];
assetRotation: [number, number, number];
speed: number;
}
@@ -63,99 +65,154 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
const updateSimulationPaths = () => {
if (!selectedActionSphere) return;
const updatedPaths: Path[] = simulationPaths.map((path) => ({
...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
),
}));
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 ConveyorEventsSchema[];
setSimulationPaths(updatedPaths);
};
return (
<group name='simulation-simulationPaths-group' ref={pathsGroupRef} >
<group name='simulation-simulationPaths-group' ref={pathsGroupRef}>
{simulationPaths.map((path) => {
const points = path.points.map(point => new THREE.Vector3(...point.position));
if (path.type === 'Conveyor') {
const points = path.points.map(point => new THREE.Vector3(...point.position));
return (
<group
name={`${path.modeluuid}-event-path`}
key={path.modeluuid}
ref={el => (groupRefs.current[path.modeluuid] = el!)}
position={path.pathPosition}
rotation={path.pathRotation}
onClick={(e) => {
if (isConnecting) return;
e.stopPropagation();
setSelectedPath({ path, group: groupRefs.current[path.modeluuid] });
setSelectedActionSphere(null);
setTransformMode(null);
setSubModule('mechanics');
}}
onPointerMissed={() => {
setSelectedPath(null);
setSubModule('properties');
}}
>
{path.points.map((point, index) => (
return (
<group
name={`${path.modeluuid}-event-path`}
key={path.modeluuid}
ref={el => (groupRefs.current[path.modeluuid] = el!)}
position={path.assetPosition}
rotation={path.assetRotation}
onClick={(e) => {
if (isConnecting) return;
e.stopPropagation();
setSelectedPath({ path, group: groupRefs.current[path.modeluuid] });
setSelectedActionSphere(null);
setTransformMode(null);
setSubModule('mechanics');
}}
onPointerMissed={() => {
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) return;
e.stopPropagation();
setSelectedActionSphere({
path,
point: sphereRefs.current[point.uuid]
});
setSubModule('mechanics');
setSelectedPath(null);
}}
userData={{ point, path }}
onPointerMissed={() => {
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.assetPosition}
onClick={(e) => {
if (isConnecting) return;
e.stopPropagation();
setSelectedPath({ path, group: groupRefs.current[path.modeluuid] });
setSelectedActionSphere(null);
setTransformMode(null);
setSubModule('mechanics');
}}
onPointerMissed={() => {
setSelectedPath(null);
setSubModule('properties');
}}
>
<Sphere
key={point.uuid}
uuid={point.uuid}
position={point.position}
key={path.point.uuid}
uuid={path.point.uuid}
position={path.point.position}
args={[0.15, 32, 32]}
name='action-sphere'
ref={el => (sphereRefs.current[point.uuid] = el!)}
name='events-sphere'
ref={el => (sphereRefs.current[path.point.uuid] = el!)}
onClick={(e) => {
if (isConnecting) return;
e.stopPropagation();
setSelectedActionSphere({
path,
point: sphereRefs.current[point.uuid]
point: sphereRefs.current[path.point.uuid]
});
setSubModule('mechanics');
setSelectedPath(null);
}}
userData={{ point, path }}
userData={{ point: path.point, path }}
onPointerMissed={() => {
setSubModule('properties');
setSelectedActionSphere(null)
setSelectedActionSphere(null);
}}
>
<meshStandardMaterial
color={index === 0 ? 'orange' : index === path.points.length - 1 ? 'blue' : 'green'}
/>
<meshStandardMaterial color="purple" />
</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>
);
</group>
);
}
return null;
})}
{selectedActionSphere && transformMode && (
@@ -163,7 +220,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
ref={transformRef}
object={selectedActionSphere.point}
mode={transformMode}
onObjectChange={updateSimulationPaths}
onMouseUp={updateSimulationPaths}
/>
)}
</group>

View File

@@ -14,7 +14,7 @@ function Simulation() {
const [processes, setProcesses] = useState([]);
useEffect(() => {
console.log('simulationPaths: ', simulationPaths);
}, [simulationPaths]);
// useEffect(() => {
@@ -31,7 +31,7 @@ function Simulation() {
return (
<>
<Behaviour setSimulationPaths={setSimulationPaths} />
<Behaviour/>
{activeModule === 'simulation' && (
<>
<PathCreation pathsGroupRef={pathsGroupRef} />

View File

@@ -1,409 +1,409 @@
import { useMemo, useState } from 'react';
import { useSelectedActionSphere, useToggleView, useSimulationPaths, useSelectedPath, useStartSimulation, useDrawMaterialPath } from '../../store/store';
import * as THREE from 'three';
import useModuleStore from '../../store/useModuleStore';
// import { useMemo, useState } from 'react';
// import { useSelectedActionSphere, useToggleView, useSimulationPaths, useSelectedPath, useStartSimulation, useDrawMaterialPath } from '../../store/store';
// import * as THREE from 'three';
// import useModuleStore from '../../store/useModuleStore';
function SimulationUI() {
const { ToggleView } = useToggleView();
const { activeModule } = useModuleStore();
const { startSimulation, setStartSimulation } = useStartSimulation();
const { selectedActionSphere } = useSelectedActionSphere();
const { selectedPath, setSelectedPath } = useSelectedPath();
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const { drawMaterialPath, setDrawMaterialPath } = useDrawMaterialPath();
const [activeButton, setActiveButton] = useState<string | null>(null);
// function SimulationUI() {
// const { ToggleView } = useToggleView();
// const { activeModule } = useModuleStore();
// const { startSimulation, setStartSimulation } = useStartSimulation();
// const { selectedActionSphere } = useSelectedActionSphere();
// const { selectedPath, setSelectedPath } = useSelectedPath();
// const { simulationPaths, setSimulationPaths } = useSimulationPaths();
// const { drawMaterialPath, setDrawMaterialPath } = useDrawMaterialPath();
// const [activeButton, setActiveButton] = useState<string | null>(null);
const handleAddAction = () => {
if (!selectedActionSphere) return;
// const handleAddAction = () => {
// if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) => {
if (point.uuid === selectedActionSphere.point.uuid) {
const actionIndex = point.actions.length;
const newAction = {
uuid: THREE.MathUtils.generateUUID(),
name: `Action ${actionIndex + 1}`, // Assign action name based on index
type: 'Inherit',
material: 'Inherit',
delay: 'Inherit',
spawnInterval: 'Inherit',
isUsed: false
};
// const updatedPaths = simulationPaths.map((path) => ({
// ...path,
// points: path.points.map((point) => {
// if (point.uuid === selectedActionSphere.point.uuid) {
// const actionIndex = point.actions.length;
// const newAction = {
// uuid: THREE.MathUtils.generateUUID(),
// name: `Action ${actionIndex + 1}`, // Assign action name based on index
// type: 'Inherit',
// material: 'Inherit',
// delay: 'Inherit',
// spawnInterval: 'Inherit',
// isUsed: false
// };
return { ...point, actions: [...point.actions, newAction] };
}
return point;
}),
}));
// return { ...point, actions: [...point.actions, newAction] };
// }
// return point;
// }),
// }));
setSimulationPaths(updatedPaths);
};
// setSimulationPaths(updatedPaths);
// };
const handleDeleteAction = (uuid: string) => {
if (!selectedActionSphere) return;
// const handleDeleteAction = (uuid: string) => {
// if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? { ...point, actions: point.actions.filter(action => action.uuid !== uuid) }
: point
),
}));
// const updatedPaths = simulationPaths.map((path) => ({
// ...path,
// points: path.points.map((point) =>
// point.uuid === selectedActionSphere.point.uuid
// ? { ...point, actions: point.actions.filter(action => action.uuid !== uuid) }
// : point
// ),
// }));
setSimulationPaths(updatedPaths);
};
// setSimulationPaths(updatedPaths);
// };
const handleActionSelect = (uuid: string, actionType: string) => {
if (!selectedActionSphere) return;
// const handleActionSelect = (uuid: string, actionType: string) => {
// if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid ? { ...action, type: actionType } : action
),
}
: point
),
}));
// const updatedPaths = simulationPaths.map((path) => ({
// ...path,
// points: path.points.map((point) =>
// point.uuid === selectedActionSphere.point.uuid
// ? {
// ...point,
// actions: point.actions.map((action) =>
// action.uuid === uuid ? { ...action, type: actionType } : action
// ),
// }
// : point
// ),
// }));
setSimulationPaths(updatedPaths);
};
// setSimulationPaths(updatedPaths);
// };
const handleMaterialSelect = (uuid: string, material: string) => {
if (!selectedActionSphere) return;
// const handleMaterialSelect = (uuid: string, material: string) => {
// if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid ? { ...action, material } : action
),
}
: point
),
}));
// const updatedPaths = simulationPaths.map((path) => ({
// ...path,
// points: path.points.map((point) =>
// point.uuid === selectedActionSphere.point.uuid
// ? {
// ...point,
// actions: point.actions.map((action) =>
// action.uuid === uuid ? { ...action, material } : action
// ),
// }
// : point
// ),
// }));
setSimulationPaths(updatedPaths);
};
// setSimulationPaths(updatedPaths);
// };
const handleDelayChange = (uuid: string, delay: number | string) => {
if (!selectedActionSphere) return;
// const handleDelayChange = (uuid: string, delay: number | string) => {
// if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid ? { ...action, delay } : action
),
}
: point
),
}));
// const updatedPaths = simulationPaths.map((path) => ({
// ...path,
// points: path.points.map((point) =>
// point.uuid === selectedActionSphere.point.uuid
// ? {
// ...point,
// actions: point.actions.map((action) =>
// action.uuid === uuid ? { ...action, delay } : action
// ),
// }
// : point
// ),
// }));
setSimulationPaths(updatedPaths);
};
// setSimulationPaths(updatedPaths);
// };
const handleSpawnIntervalChange = (uuid: string, spawnInterval: number | string) => {
if (!selectedActionSphere) return;
// const handleSpawnIntervalChange = (uuid: string, spawnInterval: number | string) => {
// if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid ? { ...action, spawnInterval } : action
),
}
: point
),
}));
// const updatedPaths = simulationPaths.map((path) => ({
// ...path,
// points: path.points.map((point) =>
// point.uuid === selectedActionSphere.point.uuid
// ? {
// ...point,
// actions: point.actions.map((action) =>
// action.uuid === uuid ? { ...action, spawnInterval } : action
// ),
// }
// : point
// ),
// }));
setSimulationPaths(updatedPaths);
};
// setSimulationPaths(updatedPaths);
// };
const handleSpeedChange = (speed: number) => {
if (!selectedPath) return;
// const handleSpeedChange = (speed: number) => {
// if (!selectedPath) return;
const updatedPaths = simulationPaths.map((path) =>
path.modeluuid === selectedPath.path.modeluuid ? { ...path, speed } : path
);
// const updatedPaths = simulationPaths.map((path) =>
// path.modeluuid === selectedPath.path.modeluuid ? { ...path, speed } : path
// );
setSimulationPaths(updatedPaths);
setSelectedPath({ ...selectedPath, path: { ...selectedPath.path, speed } });
};
// setSimulationPaths(updatedPaths);
// setSelectedPath({ ...selectedPath, path: { ...selectedPath.path, speed } });
// };
const handleAddTrigger = () => {
if (!selectedActionSphere) return;
// const handleAddTrigger = () => {
// if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) => {
if (point.uuid === selectedActionSphere.point.uuid) {
const triggerIndex = point.triggers.length;
const newTrigger = {
uuid: THREE.MathUtils.generateUUID(),
name: `Trigger ${triggerIndex + 1}`, // Assign name based on index
type: '',
isUsed: false
};
// const updatedPaths = simulationPaths.map((path) => ({
// ...path,
// points: path.points.map((point) => {
// if (point.uuid === selectedActionSphere.point.uuid) {
// const triggerIndex = point.triggers.length;
// const newTrigger = {
// uuid: THREE.MathUtils.generateUUID(),
// name: `Trigger ${triggerIndex + 1}`, // Assign name based on index
// type: '',
// isUsed: false
// };
return { ...point, triggers: [...point.triggers, newTrigger] };
}
return point;
}),
}));
// return { ...point, triggers: [...point.triggers, newTrigger] };
// }
// return point;
// }),
// }));
setSimulationPaths(updatedPaths);
};
// setSimulationPaths(updatedPaths);
// };
const handleDeleteTrigger = (uuid: string) => {
if (!selectedActionSphere) return;
// const handleDeleteTrigger = (uuid: string) => {
// if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? { ...point, triggers: point.triggers.filter(trigger => trigger.uuid !== uuid) }
: point
),
}));
// const updatedPaths = simulationPaths.map((path) => ({
// ...path,
// points: path.points.map((point) =>
// point.uuid === selectedActionSphere.point.uuid
// ? { ...point, triggers: point.triggers.filter(trigger => trigger.uuid !== uuid) }
// : point
// ),
// }));
setSimulationPaths(updatedPaths);
};
// setSimulationPaths(updatedPaths);
// };
const handleTriggerSelect = (uuid: string, triggerType: string) => {
if (!selectedActionSphere) return;
// const handleTriggerSelect = (uuid: string, triggerType: string) => {
// if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
triggers: point.triggers.map((trigger) =>
trigger.uuid === uuid ? { ...trigger, type: triggerType } : trigger
),
}
: point
),
}));
// const updatedPaths = simulationPaths.map((path) => ({
// ...path,
// points: path.points.map((point) =>
// point.uuid === selectedActionSphere.point.uuid
// ? {
// ...point,
// triggers: point.triggers.map((trigger) =>
// trigger.uuid === uuid ? { ...trigger, type: triggerType } : trigger
// ),
// }
// : point
// ),
// }));
setSimulationPaths(updatedPaths);
};
// setSimulationPaths(updatedPaths);
// };
const handleResetPath = () => {
if (!selectedPath) return;
// const handleResetPath = () => {
// if (!selectedPath) return;
};
// };
const handleActionToggle = (uuid: string) => {
if (!selectedActionSphere) return;
// const handleActionToggle = (uuid: string) => {
// if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) => ({
...action,
isUsed: action.uuid === uuid ? !action.isUsed : false,
})),
}
: point
),
}));
// const updatedPaths = simulationPaths.map((path) => ({
// ...path,
// points: path.points.map((point) =>
// point.uuid === selectedActionSphere.point.uuid
// ? {
// ...point,
// actions: point.actions.map((action) => ({
// ...action,
// isUsed: action.uuid === uuid ? !action.isUsed : false,
// })),
// }
// : point
// ),
// }));
setSimulationPaths(updatedPaths);
};
// setSimulationPaths(updatedPaths);
// };
const handleTriggerToggle = (uuid: string) => {
if (!selectedActionSphere) return;
// const handleTriggerToggle = (uuid: string) => {
// if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
triggers: point.triggers.map((trigger) => ({
...trigger,
isUsed: trigger.uuid === uuid ? !trigger.isUsed : false,
})),
}
: point
),
}));
// const updatedPaths = simulationPaths.map((path) => ({
// ...path,
// points: path.points.map((point) =>
// point.uuid === selectedActionSphere.point.uuid
// ? {
// ...point,
// triggers: point.triggers.map((trigger) => ({
// ...trigger,
// isUsed: trigger.uuid === uuid ? !trigger.isUsed : false,
// })),
// }
// : point
// ),
// }));
setSimulationPaths(updatedPaths);
};
// setSimulationPaths(updatedPaths);
// };
const selectedPoint = useMemo(() => {
if (!selectedActionSphere) return null;
return simulationPaths.flatMap((path) => path.points).find((point) => point.uuid === selectedActionSphere.point.uuid);
}, [selectedActionSphere, simulationPaths]);
// const selectedPoint = useMemo(() => {
// if (!selectedActionSphere) return null;
// return simulationPaths.flatMap((path) => path.points).find((point) => point.uuid === selectedActionSphere.point.uuid);
// }, [selectedActionSphere, simulationPaths]);
const createPath = () => {
setActiveButton(activeButton !== 'addMaterialPath' ? 'addMaterialPath' : null);
setDrawMaterialPath(!drawMaterialPath);
}
// const createPath = () => {
// setActiveButton(activeButton !== 'addMaterialPath' ? 'addMaterialPath' : null);
// setDrawMaterialPath(!drawMaterialPath);
// }
return (
<>
{activeModule === "simulation" && (
<div style={{ zIndex: 10, position: "fixed", width: '260px' }}>
{!ToggleView && (
<>
<button
onClick={() => setStartSimulation(!startSimulation)}
style={{
marginTop: "10px",
background: startSimulation ? '#ff320e' : '',
padding: "10px",
borderRadius: "5px"
}}
>
{startSimulation ? 'Stop Simulation' : 'Start Simulation'}
</button>
// return (
// <>
// {activeModule === "simulation" && (
// <div style={{ zIndex: 10, position: "fixed", width: '260px' }}>
// {!ToggleView && (
// <>
// <button
// onClick={() => setStartSimulation(!startSimulation)}
// style={{
// marginTop: "10px",
// background: startSimulation ? '#ff320e' : '',
// padding: "10px",
// borderRadius: "5px"
// }}
// >
// {startSimulation ? 'Stop Simulation' : 'Start Simulation'}
// </button>
<div style={{ zIndex: "10", position: "relative" }}>
{!ToggleView && <button onClick={createPath} style={{ marginTop: "10px", background: activeButton === 'addMaterialPath' ? '#ff320e' : '' }}> Add Material Path</button>}
</div>
// <div style={{ zIndex: "10", position: "relative" }}>
// {!ToggleView && <button onClick={createPath} style={{ marginTop: "10px", background: activeButton === 'addMaterialPath' ? '#ff320e' : '' }}> Add Material Path</button>}
// </div>
{selectedPath && (
<div style={{ marginTop: "10px" }}>
<label>Path Speed:</label>
<input
style={{ width: '50px' }}
type="number"
value={selectedPath.path.speed}
min="0.1"
step="0.1"
onChange={(e) => handleSpeedChange(parseFloat(e.target.value))}
/>
</div>
)}
// {selectedPath && (
// <div style={{ marginTop: "10px" }}>
// <label>Path Speed:</label>
// <input
// style={{ width: '50px' }}
// type="number"
// value={selectedPath.path.speed}
// min="0.1"
// step="0.1"
// onChange={(e) => handleSpeedChange(parseFloat(e.target.value))}
// />
// </div>
// )}
{selectedActionSphere && (
<div style={{ marginTop: "10px" }}>
<button onClick={handleAddAction}>Add Action</button>
<button onClick={handleAddTrigger}>Add Trigger</button>
// {selectedActionSphere && (
// <div style={{ marginTop: "10px" }}>
// <button onClick={handleAddAction}>Add Action</button>
// <button onClick={handleAddTrigger}>Add Trigger</button>
{selectedPoint?.actions.map((action) => (
<div key={action.uuid} style={{ marginTop: "10px" }}>
<select value={action.type} onChange={(e) => handleActionSelect(action.uuid, e.target.value)}>
<option value="Inherit">Inherit</option>
<option value="Spawn">Spawn Point</option>
<option value="Swap">Swap Material</option>
<option value="Despawn">Despawn Point</option>
<option value="Delay">Delay</option>
</select>
<button onClick={() => handleDeleteAction(action.uuid)}>Delete Action</button>
<label>
<input
type="checkbox"
checked={action.isUsed}
onChange={() => handleActionToggle(action.uuid)}
/>
</label>
// {selectedPoint?.actions.map((action) => (
// <div key={action.uuid} style={{ marginTop: "10px" }}>
// <select value={action.type} onChange={(e) => handleActionSelect(action.uuid, e.target.value)}>
// <option value="Inherit">Inherit</option>
// <option value="Spawn">Spawn Point</option>
// <option value="Swap">Swap Material</option>
// <option value="Despawn">Despawn Point</option>
// <option value="Delay">Delay</option>
// </select>
// <button onClick={() => handleDeleteAction(action.uuid)}>Delete Action</button>
// <label>
// <input
// type="checkbox"
// checked={action.isUsed}
// onChange={() => handleActionToggle(action.uuid)}
// />
// </label>
{(action.type === 'Spawn' || action.type === 'Swap') && (
<div style={{ marginTop: "10px" }}>
<select value={action.material} onChange={(e) => handleMaterialSelect(action.uuid, e.target.value)}>
<option value="Inherit">Inherit</option>
<option value="Crate">Crate</option>
<option value="Box">Box</option>
</select>
</div>
)}
// {(action.type === 'Spawn' || action.type === 'Swap') && (
// <div style={{ marginTop: "10px" }}>
// <select value={action.material} onChange={(e) => handleMaterialSelect(action.uuid, e.target.value)}>
// <option value="Inherit">Inherit</option>
// <option value="Crate">Crate</option>
// <option value="Box">Box</option>
// </select>
// </div>
// )}
{action.type === 'Delay' && (
<div style={{ marginTop: "10px" }}>
<label>Delay Time:</label>
<input
style={{ width: '50px' }}
type="text"
value={isNaN(Number(action.delay)) || action.delay === "Inherit" ? "Inherit" : action.delay}
min="1"
onChange={(e) => handleDelayChange(action.uuid, parseInt(e.target.value) || 'Inherit')}
/>
// {action.type === 'Delay' && (
// <div style={{ marginTop: "10px" }}>
// <label>Delay Time:</label>
// <input
// style={{ width: '50px' }}
// type="text"
// value={isNaN(Number(action.delay)) || action.delay === "Inherit" ? "Inherit" : action.delay}
// min="1"
// onChange={(e) => handleDelayChange(action.uuid, parseInt(e.target.value) || 'Inherit')}
// />
</div>
)}
// </div>
// )}
{action.type === 'Spawn' && (
<div style={{ marginTop: "10px" }}>
<label>Spawn Interval:</label>
<input
style={{ width: '50px' }}
type="text"
value={isNaN(Number(action.spawnInterval)) || action.spawnInterval === "Inherit" ? "Inherit" : action.spawnInterval}
min="1"
onChange={(e) => handleSpawnIntervalChange(action.uuid, parseInt(e.target.value) || 'Inherit')}
/>
// {action.type === 'Spawn' && (
// <div style={{ marginTop: "10px" }}>
// <label>Spawn Interval:</label>
// <input
// style={{ width: '50px' }}
// type="text"
// value={isNaN(Number(action.spawnInterval)) || action.spawnInterval === "Inherit" ? "Inherit" : action.spawnInterval}
// min="1"
// onChange={(e) => handleSpawnIntervalChange(action.uuid, parseInt(e.target.value) || 'Inherit')}
// />
</div>
)}
<hr style={{ margin: "10px 0", borderColor: "#ccc" }} />
</div>
))}
// </div>
// )}
// <hr style={{ margin: "10px 0", borderColor: "#ccc" }} />
// </div>
// ))}
<hr style={{ margin: "10px 0", border: "1px solid black" }} />
// <hr style={{ margin: "10px 0", border: "1px solid black" }} />
{selectedPoint?.triggers.map((trigger) => (
<div key={trigger.uuid} style={{ marginTop: "10px" }}>
<select value={trigger.type} onChange={(e) => handleTriggerSelect(trigger.uuid, e.target.value)}>
<option value="">Select Trigger Type</option>
<option value="On-Hit">On Hit</option>
<option value="Buffer">Buffer</option>
</select>
<button onClick={() => handleDeleteTrigger(trigger.uuid)}>Delete Trigger</button>
<label>
<input
type="checkbox"
checked={trigger.isUsed}
onChange={() => handleTriggerToggle(trigger.uuid)}
/>
</label>
<hr style={{ margin: "10px 0", borderColor: "#ccc" }} />
</div>
))}
// {selectedPoint?.triggers.map((trigger) => (
// <div key={trigger.uuid} style={{ marginTop: "10px" }}>
// <select value={trigger.type} onChange={(e) => handleTriggerSelect(trigger.uuid, e.target.value)}>
// <option value="">Select Trigger Type</option>
// <option value="On-Hit">On Hit</option>
// <option value="Buffer">Buffer</option>
// </select>
// <button onClick={() => handleDeleteTrigger(trigger.uuid)}>Delete Trigger</button>
// <label>
// <input
// type="checkbox"
// checked={trigger.isUsed}
// onChange={() => handleTriggerToggle(trigger.uuid)}
// />
// </label>
// <hr style={{ margin: "10px 0", borderColor: "#ccc" }} />
// </div>
// ))}
</div>
)}
// </div>
// )}
{selectedPath && (
<div style={{ marginTop: "10px" }}>
<button
onClick={handleResetPath}
style={{ padding: "10px", borderRadius: "5px", background: "#ff0000", color: "#fff" }}
>
Reset Path
</button>
</div>
)}
</>
)}
</div>
)}
</>
);
}
// {selectedPath && (
// <div style={{ marginTop: "10px" }}>
// <button
// onClick={handleResetPath}
// style={{ padding: "10px", borderRadius: "5px", background: "#ff0000", color: "#fff" }}
// >
// Reset Path
// </button>
// </div>
// )}
// </>
// )}
// </div>
// )}
// </>
// );
// }
export default SimulationUI;
// export default SimulationUI;