From 82a7cd0001b0efd91e7dcc91b73a1f58f0a1f720 Mon Sep 17 00:00:00 2001 From: Poovizhi99 Date: Tue, 15 Apr 2025 09:11:01 +0530 Subject: [PATCH 1/5] updated paths --- .../modules/simulation/path/pathConnector.tsx | 2562 ++++++++++------- 1 file changed, 1445 insertions(+), 1117 deletions(-) diff --git a/app/src/modules/simulation/path/pathConnector.tsx b/app/src/modules/simulation/path/pathConnector.tsx index c561cbb..f6ee51f 100644 --- a/app/src/modules/simulation/path/pathConnector.tsx +++ b/app/src/modules/simulation/path/pathConnector.tsx @@ -1,1170 +1,1498 @@ -import { useFrame, useThree } from '@react-three/fiber'; -import React, { useEffect, useRef, useState } from 'react'; -import * as THREE from 'three'; -import * as Types from '../../../types/world/worldTypes'; -import { QuadraticBezierLine } from '@react-three/drei'; -import { useDeleteTool, useIsConnecting, useRenderDistance, useSimulationStates, useSocketStore } from '../../../store/store'; -import useModuleStore from '../../../store/useModuleStore'; -import { usePlayButtonStore } from '../../../store/usePlayButtonStore'; -import { setEventApi } from '../../../services/factoryBuilder/assest/floorAsset/setEventsApt'; +import { useFrame, useThree } from "@react-three/fiber"; +import React, { useEffect, useRef, useState } from "react"; +import * as THREE from "three"; +import * as Types from "../../../types/world/worldTypes"; +import { QuadraticBezierLine } from "@react-three/drei"; +import { + useDeleteTool, + useIsConnecting, + useRenderDistance, + useSimulationStates, + useSocketStore, +} from "../../../store/store"; +import useModuleStore from "../../../store/useModuleStore"; +import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; +import { setEventApi } from "../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; -function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject }) { - const { activeModule } = useModuleStore(); - const { gl, raycaster, scene, pointer, camera } = useThree(); - const { deleteTool } = useDeleteTool(); - const { renderDistance } = useRenderDistance(); - const { setIsConnecting } = useIsConnecting(); - const { simulationStates, setSimulationStates } = useSimulationStates(); - const { isPlaying } = usePlayButtonStore(); - const { socket } = useSocketStore(); - const groupRefs = useRef<{ [key: string]: any }>({}); +function PathConnector({ + pathsGroupRef, +}: { + pathsGroupRef: React.MutableRefObject; +}) { + const { activeModule } = useModuleStore(); + const { gl, raycaster, scene, pointer, camera } = useThree(); + const { deleteTool } = useDeleteTool(); + const { renderDistance } = useRenderDistance(); + const { setIsConnecting } = useIsConnecting(); + const { simulationStates, setSimulationStates } = useSimulationStates(); + const { isPlaying } = usePlayButtonStore(); + const { socket } = useSocketStore(); + const groupRefs = useRef<{ [key: string]: any }>({}); - const [firstSelected, setFirstSelected] = useState<{ modelUUID: string; sphereUUID: string; position: THREE.Vector3; isCorner: boolean; } | null>(null); - const [currentLine, setCurrentLine] = useState<{ start: THREE.Vector3, end: THREE.Vector3, mid: THREE.Vector3 } | null>(null); - const [helperlineColor, setHelperLineColor] = useState('red'); - const [hoveredLineKey, setHoveredLineKey] = useState(null); + const [firstSelected, setFirstSelected] = useState<{ + modelUUID: string; + sphereUUID: string; + position: THREE.Vector3; + isCorner: boolean; + } | null>(null); + const [currentLine, setCurrentLine] = useState<{ + start: THREE.Vector3; + end: THREE.Vector3; + mid: THREE.Vector3; + } | null>(null); + const [helperlineColor, setHelperLineColor] = useState("red"); + const [hoveredLineKey, setHoveredLineKey] = useState(null); - const updatePathConnections = (fromModelUUID: string, fromPointUUID: string, toModelUUID: string, toPointUUID: string) => { - const updatedPaths = simulationStates.map(path => { - if (path.type === 'Conveyor') { - // Handle outgoing connections from Conveyor - if (path.modeluuid === fromModelUUID) { - return { - ...path, - points: path.points.map(point => { - if (point.uuid === fromPointUUID) { - const newTarget = { - modelUUID: toModelUUID, - pointUUID: toPointUUID - }; - const existingTargets = point.connections.targets || []; + const updatePathConnections = ( + fromModelUUID: string, + fromPointUUID: string, + toModelUUID: string, + toPointUUID: string + ) => { + const updatedPaths = simulationStates.map((path) => { + if (path.type === "Conveyor") { + // Handle outgoing connections from Conveyor + if (path.modeluuid === fromModelUUID) { + return { + ...path, + points: path.points.map((point) => { + if (point.uuid === fromPointUUID) { + const newTarget = { + modelUUID: toModelUUID, + pointUUID: toPointUUID, + }; + const existingTargets = point.connections.targets || []; - if (!existingTargets.some(target => - target.modelUUID === newTarget.modelUUID && - target.pointUUID === newTarget.pointUUID - )) { - return { - ...point, - connections: { - ...point.connections, - targets: [...existingTargets, newTarget] - } - }; - } - } - return point; - }) - }; + if ( + !existingTargets.some( + (target) => + target.modelUUID === newTarget.modelUUID && + target.pointUUID === newTarget.pointUUID + ) + ) { + return { + ...point, + connections: { + ...point.connections, + targets: [...existingTargets, newTarget], + }, + }; } - // Handle incoming connections to Conveyor - else if (path.modeluuid === toModelUUID) { - return { - ...path, - points: path.points.map(point => { - if (point.uuid === toPointUUID) { - const reverseTarget = { - modelUUID: fromModelUUID, - pointUUID: fromPointUUID - }; - const existingTargets = point.connections.targets || []; + } + return point; + }), + }; + } + // Handle incoming connections to Conveyor + else if (path.modeluuid === toModelUUID) { + return { + ...path, + points: path.points.map((point) => { + if (point.uuid === toPointUUID) { + const reverseTarget = { + modelUUID: fromModelUUID, + pointUUID: fromPointUUID, + }; + const existingTargets = point.connections.targets || []; - if (!existingTargets.some(target => - target.modelUUID === reverseTarget.modelUUID && - target.pointUUID === reverseTarget.pointUUID - )) { - return { - ...point, - connections: { - ...point.connections, - targets: [...existingTargets, reverseTarget] - } - }; - } - } - return point; - }) - }; + if ( + !existingTargets.some( + (target) => + target.modelUUID === reverseTarget.modelUUID && + target.pointUUID === reverseTarget.pointUUID + ) + ) { + return { + ...point, + connections: { + ...point.connections, + targets: [...existingTargets, reverseTarget], + }, + }; } - } - else if (path.type === 'Vehicle') { - // Handle outgoing connections from Vehicle - if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) { - const newTarget = { - modelUUID: toModelUUID, - pointUUID: toPointUUID - }; - const existingTargets = path.points.connections.targets || []; - - // Check if target is a Conveyor - const toPath = simulationStates.find(p => p.modeluuid === toModelUUID); - if (toPath?.type !== 'Conveyor') { - console.log("Vehicle can only connect to Conveyors"); - return path; - } - - // Check if already has a connection - if (existingTargets.length >= 1) { - console.log("Vehicle can have only one connection"); - return path; - } - - if (!existingTargets.some(target => - target.modelUUID === newTarget.modelUUID && - target.pointUUID === newTarget.pointUUID - )) { - return { - ...path, - points: { - ...path.points, - connections: { - ...path.points.connections, - targets: [...existingTargets, newTarget] - } - } - }; - } - } - // Handle incoming connections to Vehicle - else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) { - const reverseTarget = { - modelUUID: fromModelUUID, - pointUUID: fromPointUUID - }; - const existingTargets = path.points.connections.targets || []; - - // Check if source is a Conveyor - const fromPath = simulationStates.find(p => p.modeluuid === fromModelUUID); - if (fromPath?.type !== 'Conveyor') { - console.log("Vehicle can only connect to Conveyors"); - return path; - } - - // Check if already has a connection - if (existingTargets.length >= 1) { - console.log("Vehicle can have only one connection"); - return path; - } - - if (!existingTargets.some(target => - target.modelUUID === reverseTarget.modelUUID && - target.pointUUID === reverseTarget.pointUUID - )) { - return { - ...path, - points: { - ...path.points, - connections: { - ...path.points.connections, - targets: [...existingTargets, reverseTarget] - } - } - }; - } - } - return path; - } - else if (path.type === 'StaticMachine') { - // Handle outgoing connections from StaticMachine - if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) { - const newTarget = { - modelUUID: toModelUUID, - pointUUID: toPointUUID - }; - - // Ensure target is an ArmBot - const toPath = simulationStates.find(p => p.modeluuid === toModelUUID); - if (toPath?.type !== 'ArmBot') { - console.log("StaticMachine can only connect to ArmBot"); - return path; - } - - const existingTargets = path.points.connections.targets || []; - - // Allow only one connection - if (existingTargets.length >= 1) { - console.log("StaticMachine can only have one connection"); - return path; - } - - if (!existingTargets.some(target => - target.modelUUID === newTarget.modelUUID && - target.pointUUID === newTarget.pointUUID - )) { - return { - ...path, - points: { - ...path.points, - connections: { - ...path.points.connections, - targets: [...existingTargets, newTarget] - } - } - }; - } - } - - // Handle incoming connections to StaticMachine - else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) { - const reverseTarget = { - modelUUID: fromModelUUID, - pointUUID: fromPointUUID - }; - - const fromPath = simulationStates.find(p => p.modeluuid === fromModelUUID); - if (fromPath?.type !== 'ArmBot') { - console.log("StaticMachine can only be connected from ArmBot"); - return path; - } - - const existingTargets = path.points.connections.targets || []; - - if (existingTargets.length >= 1) { - console.log("StaticMachine can only have one connection"); - return path; - } - - if (!existingTargets.some(target => - target.modelUUID === reverseTarget.modelUUID && - target.pointUUID === reverseTarget.pointUUID - )) { - return { - ...path, - points: { - ...path.points, - connections: { - ...path.points.connections, - targets: [...existingTargets, reverseTarget] - } - } - }; - } - } - return path; - } - else if (path.type === 'ArmBot') { - // Handle outgoing connections from ArmBot - if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) { - const newTarget = { - modelUUID: toModelUUID, - pointUUID: toPointUUID - }; - - const toPath = simulationStates.find(p => p.modeluuid === toModelUUID); - if (!toPath) return path; - - const existingTargets = path.points.connections.targets || []; - - // Check if connecting to a StaticMachine and already connected to one - const alreadyConnectedToStatic = existingTargets.some(target => { - const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID); - return targetPath?.type === 'StaticMachine'; - }); - - if (toPath.type === 'StaticMachine') { - if (alreadyConnectedToStatic) { - console.log("ArmBot can only connect to one StaticMachine"); - return path; - } - } - - if (!existingTargets.some(target => - target.modelUUID === newTarget.modelUUID && - target.pointUUID === newTarget.pointUUID - )) { - return { - ...path, - points: { - ...path.points, - connections: { - ...path.points.connections, - targets: [...existingTargets, newTarget] - } - } - }; - } - } - - // Handle incoming connections to ArmBot - else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) { - const reverseTarget = { - modelUUID: fromModelUUID, - pointUUID: fromPointUUID - }; - - const fromPath = simulationStates.find(p => p.modeluuid === fromModelUUID); - if (!fromPath) return path; - - const existingTargets = path.points.connections.targets || []; - - const alreadyConnectedFromStatic = existingTargets.some(target => { - const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID); - return targetPath?.type === 'StaticMachine'; - }); - - if (fromPath.type === 'StaticMachine') { - if (alreadyConnectedFromStatic) { - console.log("ArmBot can only be connected from one StaticMachine"); - return path; - } - } - - if (!existingTargets.some(target => - target.modelUUID === reverseTarget.modelUUID && - target.pointUUID === reverseTarget.pointUUID - )) { - return { - ...path, - points: { - ...path.points, - connections: { - ...path.points.connections, - targets: [...existingTargets, reverseTarget] - } - } - }; - } - } - return path; - } + } + return point; + }), + }; + } + } else if (path.type === "Vehicle") { + // Handle outgoing connections from Vehicle + if ( + path.modeluuid === fromModelUUID && + path.points.uuid === fromPointUUID + ) { + const newTarget = { + modelUUID: toModelUUID, + pointUUID: toPointUUID, + }; + const existingTargets = path.points.connections.targets || []; + // Check if target is a Conveyor + const toPath = simulationStates.find( + (p) => p.modeluuid === toModelUUID + ); + if (toPath?.type !== "Conveyor") { + console.log("Vehicle can only connect to Conveyors"); return path; - }); + } - setSimulationStates(updatedPaths); + // Check if already has a connection + if (existingTargets.length >= 1) { + console.log("Vehicle can have only one connection"); + return path; + } - const updatedPathDetails = updatedPaths.filter(path => - path.modeluuid === fromModelUUID || path.modeluuid === toModelUUID - ); + if ( + !existingTargets.some( + (target) => + target.modelUUID === newTarget.modelUUID && + target.pointUUID === newTarget.pointUUID + ) + ) { + return { + ...path, + points: { + ...path.points, + connections: { + ...path.points.connections, + targets: [...existingTargets, newTarget], + }, + }, + }; + } + } + // Handle incoming connections to Vehicle + else if ( + path.modeluuid === toModelUUID && + path.points.uuid === toPointUUID + ) { + const reverseTarget = { + modelUUID: fromModelUUID, + pointUUID: fromPointUUID, + }; + const existingTargets = path.points.connections.targets || []; - updateBackend(updatedPathDetails); + // Check if source is a Conveyor + const fromPath = simulationStates.find( + (p) => p.modeluuid === fromModelUUID + ); + if (fromPath?.type !== "Conveyor") { + console.log("Vehicle can only connect to Conveyors"); + return path; + } + + // Check if already has a connection + if (existingTargets.length >= 1) { + console.log("Vehicle can have only one connection"); + return path; + } + + if ( + !existingTargets.some( + (target) => + target.modelUUID === reverseTarget.modelUUID && + target.pointUUID === reverseTarget.pointUUID + ) + ) { + return { + ...path, + points: { + ...path.points, + connections: { + ...path.points.connections, + targets: [...existingTargets, reverseTarget], + }, + }, + }; + } + } + return path; + } else if (path.type === "StaticMachine") { + // Handle outgoing connections from StaticMachine + if ( + path.modeluuid === fromModelUUID && + path.points.uuid === fromPointUUID + ) { + const newTarget = { + modelUUID: toModelUUID, + pointUUID: toPointUUID, + }; + + // Ensure target is an ArmBot + const toPath = simulationStates.find( + (p) => p.modeluuid === toModelUUID + ); + if (toPath?.type !== "ArmBot") { + console.log("StaticMachine can only connect to ArmBot"); + return path; + } + + const existingTargets = path.points.connections.targets || []; + + // Allow only one connection + if (existingTargets.length >= 1) { + console.log("StaticMachine can only have one connection"); + return path; + } + + if ( + !existingTargets.some( + (target) => + target.modelUUID === newTarget.modelUUID && + target.pointUUID === newTarget.pointUUID + ) + ) { + return { + ...path, + points: { + ...path.points, + connections: { + ...path.points.connections, + targets: [...existingTargets, newTarget], + }, + }, + }; + } + } + + // Handle incoming connections to StaticMachine + else if ( + path.modeluuid === toModelUUID && + path.points.uuid === toPointUUID + ) { + const reverseTarget = { + modelUUID: fromModelUUID, + pointUUID: fromPointUUID, + }; + + const fromPath = simulationStates.find( + (p) => p.modeluuid === fromModelUUID + ); + if (fromPath?.type !== "ArmBot") { + console.log("StaticMachine can only be connected from ArmBot"); + return path; + } + + const existingTargets = path.points.connections.targets || []; + + if (existingTargets.length >= 1) { + console.log("StaticMachine can only have one connection"); + return path; + } + + if ( + !existingTargets.some( + (target) => + target.modelUUID === reverseTarget.modelUUID && + target.pointUUID === reverseTarget.pointUUID + ) + ) { + return { + ...path, + points: { + ...path.points, + connections: { + ...path.points.connections, + targets: [...existingTargets, reverseTarget], + }, + }, + }; + } + } + return path; + } else if (path.type === "ArmBot") { + // Handle outgoing connections from ArmBot + if ( + path.modeluuid === fromModelUUID && + path.points.uuid === fromPointUUID + ) { + const newTarget = { + modelUUID: toModelUUID, + pointUUID: toPointUUID, + }; + + const toPath = simulationStates.find( + (p) => p.modeluuid === toModelUUID + ); + if (!toPath) return path; + + const existingTargets = path.points.connections.targets || []; + + // Check if connecting to a StaticMachine and already connected to one + const alreadyConnectedToStatic = existingTargets.some((target) => { + const targetPath = simulationStates.find( + (p) => p.modeluuid === target.modelUUID + ); + return targetPath?.type === "StaticMachine"; + }); + + if (toPath.type === "StaticMachine") { + if (alreadyConnectedToStatic) { + console.log("ArmBot can only connect to one StaticMachine"); + return path; + } + } + + if ( + !existingTargets.some( + (target) => + target.modelUUID === newTarget.modelUUID && + target.pointUUID === newTarget.pointUUID + ) + ) { + return { + ...path, + points: { + ...path.points, + connections: { + ...path.points.connections, + targets: [...existingTargets, newTarget], + }, + }, + }; + } + } + + // Handle incoming connections to ArmBot + else if ( + path.modeluuid === toModelUUID && + path.points.uuid === toPointUUID + ) { + const reverseTarget = { + modelUUID: fromModelUUID, + pointUUID: fromPointUUID, + }; + + const fromPath = simulationStates.find( + (p) => p.modeluuid === fromModelUUID + ); + if (!fromPath) return path; + + const existingTargets = path.points.connections.targets || []; + + const alreadyConnectedFromStatic = existingTargets.some((target) => { + const targetPath = simulationStates.find( + (p) => p.modeluuid === target.modelUUID + ); + return targetPath?.type === "StaticMachine"; + }); + + if (fromPath.type === "StaticMachine") { + if (alreadyConnectedFromStatic) { + console.log( + "ArmBot can only be connected from one StaticMachine" + ); + return path; + } + } + + if ( + !existingTargets.some( + (target) => + target.modelUUID === reverseTarget.modelUUID && + target.pointUUID === reverseTarget.pointUUID + ) + ) { + return { + ...path, + points: { + ...path.points, + connections: { + ...path.points.connections, + targets: [...existingTargets, reverseTarget], + }, + }, + }; + } + } + return path; + } + + return path; + }); + + setSimulationStates(updatedPaths); + + const updatedPathDetails = updatedPaths.filter( + (path) => + path.modeluuid === fromModelUUID || path.modeluuid === toModelUUID + ); + + updateBackend(updatedPathDetails); + }; + + const updateBackend = async ( + updatedPaths: ( + | Types.ConveyorEventsSchema + | Types.VehicleEventsSchema + | Types.StaticMachineEventsSchema + | Types.ArmBotEventsSchema + )[] + ) => { + if (updatedPaths.length === 0) return; + const email = localStorage.getItem("email"); + const organization = email ? email.split("@")[1].split(".")[0] : ""; + + updatedPaths.forEach(async (updatedPath) => { + if (updatedPath.type === "Conveyor") { + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed } + // ); + + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { + type: "Conveyor", + points: updatedPath.points, + speed: updatedPath.speed, + }, + }; + + socket.emit("v2:model-asset:updateEventData", data); + } else if (updatedPath.type === "Vehicle") { + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "Vehicle", points: updatedPath.points } + // ); + + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { type: "Vehicle", points: updatedPath.points }, + }; + + socket.emit("v2:model-asset:updateEventData", data); + } else if (updatedPath.type === "StaticMachine") { + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "StaticMachine", points: updatedPath.points } + // ); + + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { type: "StaticMachine", points: updatedPath.points }, + }; + + socket.emit("v2:model-asset:updateEventData", data); + } else if (updatedPath.type === "ArmBot") { + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "ArmBot", points: updatedPath.points } + // ); + + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { type: "ArmBot", points: updatedPath.points }, + }; + + socket.emit("v2:model-asset:updateEventData", data); + } + }); + }; + + const handleAddConnection = ( + fromModelUUID: string, + fromUUID: string, + toModelUUID: string, + toUUID: string + ) => { + updatePathConnections(fromModelUUID, fromUUID, toModelUUID, toUUID); + setFirstSelected(null); + setCurrentLine(null); + setIsConnecting(false); + }; + + useEffect(() => { + const canvasElement = gl.domElement; + let drag = false; + let MouseDown = false; + + const onMouseDown = () => { + MouseDown = true; + drag = false; }; - const updateBackend = async (updatedPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => { - if (updatedPaths.length === 0) return; - const email = localStorage.getItem("email"); - const organization = email ? email.split("@")[1].split(".")[0] : ""; + const onMouseUp = () => { + MouseDown = false; + }; - updatedPaths.forEach(async (updatedPath) => { - if (updatedPath.type === 'Conveyor') { + const onMouseMove = () => { + if (MouseDown) { + drag = true; + } + }; - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed } - // ); + const onContextMenu = (evt: MouseEvent) => { + evt.preventDefault(); + if (drag || evt.button === 0) return; - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed } - } + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster.intersectObjects( + pathsGroupRef.current.children, + true + ); - socket.emit('v2:model-asset:updateEventData', data); + if (intersects.length > 0) { + const intersected = intersects[0].object; - } else if (updatedPath.type === 'Vehicle') { + if (intersected.name.includes("events-sphere")) { + const modelUUID = intersected.userData.path.modeluuid; + const sphereUUID = intersected.uuid; + const worldPosition = new THREE.Vector3(); + intersected.getWorldPosition(worldPosition); - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "Vehicle", points: updatedPath.points } - // ); + let isStartOrEnd = false; - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "Vehicle", points: updatedPath.points } - } + if ( + intersected.userData.path.points && + intersected.userData.path.points.length > 1 + ) { + 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.points) { + isStartOrEnd = sphereUUID === intersected.userData.path.points.uuid; + } - socket.emit('v2:model-asset:updateEventData', data); - - } else if (updatedPath.type === 'StaticMachine') { - - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "StaticMachine", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "StaticMachine", points: updatedPath.points } - } - - socket.emit('v2:model-asset:updateEventData', data); - - } else if (updatedPath.type === 'ArmBot') { - - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "ArmBot", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "ArmBot", points: updatedPath.points } - } - - socket.emit('v2:model-asset:updateEventData', data); + if (modelUUID) { + const firstPath = simulationStates.find( + (p) => p.modeluuid === firstSelected?.modelUUID + ); + const secondPath = simulationStates.find( + (p) => p.modeluuid === modelUUID + ); + // Prevent vehicle-to-vehicle connections + if ( + firstPath && + secondPath && + firstPath.type === "Vehicle" && + secondPath.type === "Vehicle" + ) { + console.log("Cannot connect two vehicle paths together"); + return; } - }) - } + // Prevent conveyor middle point to conveyor connections + if ( + firstPath && + secondPath && + firstPath.type === "Conveyor" && + secondPath.type === "Conveyor" && + (!firstSelected?.isCorner || !isStartOrEnd) + ) { + console.log( + "Conveyor connections must be between start/end points" + ); + return; + } - const handleAddConnection = (fromModelUUID: string, fromUUID: string, toModelUUID: string, toUUID: string) => { - updatePathConnections(fromModelUUID, fromUUID, toModelUUID, toUUID); + // Check if this specific connection already exists + const isDuplicateConnection = firstSelected + ? simulationStates.some((path) => { + if (path.modeluuid === firstSelected.modelUUID) { + if (path.type === "Conveyor") { + const point = path.points.find( + (p) => p.uuid === firstSelected.sphereUUID + ); + return point?.connections.targets.some( + (t) => + t.modelUUID === modelUUID && + t.pointUUID === sphereUUID + ); + } else if (path.type === "Vehicle") { + return path.points.connections.targets.some( + (t) => + t.modelUUID === modelUUID && + t.pointUUID === sphereUUID + ); + } + } + return false; + }) + : false; + + if (isDuplicateConnection) { + console.log("These points are already connected. Ignoring."); + return; + } + + // For Vehicles, check if they're already connected to anything + if (intersected.userData.path.type === "Vehicle") { + const vehicleConnections = + intersected.userData.path.points.connections.targets.length; + if (vehicleConnections >= 1) { + console.log("Vehicle can only have one connection"); + return; + } + } + + // For non-Vehicle paths, check if already connected + if (intersected.userData.path.type !== "Vehicle") { + const isAlreadyConnected = simulationStates.some((path) => { + if (path.type === "Conveyor") { + return path.points.some( + (point) => + point.uuid === sphereUUID && + point.connections.targets.length > 0 + ); + } + return false; + }); + + if (isAlreadyConnected) { + console.log("Conveyor point is already connected. Ignoring."); + return; + } + } + + if (firstSelected) { + // Check if trying to connect Vehicle to non-Conveyor + if ( + (firstPath?.type === "Vehicle" && + secondPath?.type !== "Conveyor") || + (secondPath?.type === "Vehicle" && + firstPath?.type !== "Conveyor") + ) { + console.log("Vehicle can only connect to Conveyors"); + return; + } + + // Prevent same-path connections + if (firstSelected.modelUUID === modelUUID) { + console.log("Cannot connect spheres on the same path."); + return; + } + + // Check if StaticMachine is involved in the connection + if ( + (firstPath?.type === "StaticMachine" && + secondPath?.type !== "ArmBot") || + (secondPath?.type === "StaticMachine" && + firstPath?.type !== "ArmBot") + ) { + console.log("StaticMachine can only connect to ArmBot"); + return; + } + + // Check if StaticMachine already has a connection + if (firstPath?.type === "StaticMachine") { + const staticConnections = + firstPath.points.connections.targets.length; + if (staticConnections >= 1) { + console.log("StaticMachine can only have one connection"); + return; + } + } + if (secondPath?.type === "StaticMachine") { + const staticConnections = + secondPath.points.connections.targets.length; + if (staticConnections >= 1) { + console.log("StaticMachine can only have one connection"); + return; + } + } + + // Check if ArmBot is involved + if ( + (firstPath?.type === "ArmBot" && + secondPath?.type === "StaticMachine") || + (secondPath?.type === "ArmBot" && + firstPath?.type === "StaticMachine") + ) { + const armBotPath = + firstPath?.type === "ArmBot" ? firstPath : secondPath; + const staticPath = + firstPath?.type === "StaticMachine" ? firstPath : secondPath; + + const armBotConnections = + armBotPath.points.connections.targets || []; + const alreadyConnectedToStatic = armBotConnections.some( + (target) => { + const targetPath = simulationStates.find( + (p) => p.modeluuid === target.modelUUID + ); + return targetPath?.type === "StaticMachine"; + } + ); + + if (alreadyConnectedToStatic) { + console.log("ArmBot can only connect to one StaticMachine"); + return; + } + + const staticConnections = + staticPath.points.connections.targets.length; + if (staticConnections >= 1) { + console.log("StaticMachine can only have one connection"); + return; + } + } + + // Prevent ArmBot ↔ ArmBot + if ( + firstPath?.type === "ArmBot" && + secondPath?.type === "ArmBot" + ) { + console.log("Cannot connect two ArmBots together"); + return; + } + + // If one is ArmBot, ensure the other is StaticMachine or Conveyor + if ( + firstPath?.type === "ArmBot" || + secondPath?.type === "ArmBot" + ) { + const otherType = + firstPath?.type === "ArmBot" + ? secondPath?.type + : firstPath?.type; + if (otherType !== "StaticMachine" && otherType !== "Conveyor") { + console.log( + "ArmBot can only connect to Conveyors or one StaticMachine" + ); + return; + } + } + + // At least one must be start/end point + if (!firstSelected.isCorner && !isStartOrEnd) { + console.log( + "At least one of the selected spheres must be a start or end point." + ); + return; + } + + // All checks passed - make the connection + handleAddConnection( + firstSelected.modelUUID, + firstSelected.sphereUUID, + modelUUID, + sphereUUID + ); + } else { + // First selection - just store it + setFirstSelected({ + modelUUID, + sphereUUID, + position: worldPosition, + isCorner: isStartOrEnd, + }); + setIsConnecting(true); + } + } + } + } else { + // Clicked outside - cancel connection setFirstSelected(null); setCurrentLine(null); setIsConnecting(false); + } }; - useEffect(() => { - const canvasElement = gl.domElement; - let drag = false; - let MouseDown = false; + if (activeModule === "simulation" && !deleteTool) { + canvasElement.addEventListener("mousedown", onMouseDown); + canvasElement.addEventListener("mouseup", onMouseUp); + canvasElement.addEventListener("mousemove", onMouseMove); + canvasElement.addEventListener("contextmenu", onContextMenu); + } else { + setFirstSelected(null); + setCurrentLine(null); + setIsConnecting(false); + } - const onMouseDown = () => { - MouseDown = true; - drag = false; - }; + return () => { + canvasElement.removeEventListener("mousedown", onMouseDown); + canvasElement.removeEventListener("mouseup", onMouseUp); + canvasElement.removeEventListener("mousemove", onMouseMove); + canvasElement.removeEventListener("contextmenu", onContextMenu); + }; + }, [camera, scene, raycaster, firstSelected, simulationStates, deleteTool]); - const onMouseUp = () => { - MouseDown = false; - }; - - const onMouseMove = () => { - if (MouseDown) { - drag = true; - } - }; - - const onContextMenu = (evt: MouseEvent) => { - evt.preventDefault(); - if (drag || evt.button === 0) return; - - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster.intersectObjects(pathsGroupRef.current.children, true); - - if (intersects.length > 0) { - const intersected = intersects[0].object; - - if (intersected.name.includes("events-sphere")) { - const modelUUID = intersected.userData.path.modeluuid; - const sphereUUID = intersected.uuid; - const worldPosition = new THREE.Vector3(); - intersected.getWorldPosition(worldPosition); - - let isStartOrEnd = false; - - if (intersected.userData.path.points && intersected.userData.path.points.length > 1) { - 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.points) { - isStartOrEnd = sphereUUID === intersected.userData.path.points.uuid; - } - - if (modelUUID) { - const firstPath = simulationStates.find(p => p.modeluuid === firstSelected?.modelUUID); - const secondPath = simulationStates.find(p => p.modeluuid === modelUUID); - - // Prevent vehicle-to-vehicle connections - if (firstPath && secondPath && firstPath.type === 'Vehicle' && secondPath.type === 'Vehicle') { - console.log("Cannot connect two vehicle paths together"); - return; - } - - // Prevent conveyor middle point to conveyor connections - if (firstPath && secondPath && - firstPath.type === 'Conveyor' && - secondPath.type === 'Conveyor' && - (!firstSelected?.isCorner || !isStartOrEnd)) { - console.log("Conveyor connections must be between start/end points"); - return; - } - - // Check if this specific connection already exists - const isDuplicateConnection = firstSelected - ? simulationStates.some(path => { - if (path.modeluuid === firstSelected.modelUUID) { - if (path.type === 'Conveyor') { - const point = path.points.find(p => p.uuid === firstSelected.sphereUUID); - return point?.connections.targets.some(t => - t.modelUUID === modelUUID && t.pointUUID === sphereUUID - ); - } else if (path.type === 'Vehicle') { - return path.points.connections.targets.some(t => - t.modelUUID === modelUUID && t.pointUUID === sphereUUID - ); - } - } - return false; - }) - : false; - - if (isDuplicateConnection) { - console.log("These points are already connected. Ignoring."); - return; - } - - // For Vehicles, check if they're already connected to anything - if (intersected.userData.path.type === 'Vehicle') { - const vehicleConnections = intersected.userData.path.points.connections.targets.length; - if (vehicleConnections >= 1) { - console.log("Vehicle can only have one connection"); - return; - } - } - - // For non-Vehicle paths, check if already connected - if (intersected.userData.path.type !== 'Vehicle') { - const isAlreadyConnected = simulationStates.some(path => { - if (path.type === 'Conveyor') { - return path.points.some(point => - point.uuid === sphereUUID && - point.connections.targets.length > 0 - ); - } - return false; - }); - - if (isAlreadyConnected) { - console.log("Conveyor point is already connected. Ignoring."); - return; - } - } - - if (firstSelected) { - // Check if trying to connect Vehicle to non-Conveyor - if ((firstPath?.type === 'Vehicle' && secondPath?.type !== 'Conveyor') || - (secondPath?.type === 'Vehicle' && firstPath?.type !== 'Conveyor')) { - console.log("Vehicle can only connect to Conveyors"); - return; - } - - // Prevent same-path connections - if (firstSelected.modelUUID === modelUUID) { - console.log("Cannot connect spheres on the same path."); - return; - } - - // Check if StaticMachine is involved in the connection - if ((firstPath?.type === 'StaticMachine' && secondPath?.type !== 'ArmBot') || - (secondPath?.type === 'StaticMachine' && firstPath?.type !== 'ArmBot')) { - console.log("StaticMachine can only connect to ArmBot"); - return; - } - - // Check if StaticMachine already has a connection - if (firstPath?.type === 'StaticMachine') { - const staticConnections = firstPath.points.connections.targets.length; - if (staticConnections >= 1) { - console.log("StaticMachine can only have one connection"); - return; - } - } - if (secondPath?.type === 'StaticMachine') { - const staticConnections = secondPath.points.connections.targets.length; - if (staticConnections >= 1) { - console.log("StaticMachine can only have one connection"); - return; - } - } - - // Check if ArmBot is involved - if ((firstPath?.type === 'ArmBot' && secondPath?.type === 'StaticMachine') || - (secondPath?.type === 'ArmBot' && firstPath?.type === 'StaticMachine')) { - - const armBotPath = firstPath?.type === 'ArmBot' ? firstPath : secondPath; - const staticPath = firstPath?.type === 'StaticMachine' ? firstPath : secondPath; - - const armBotConnections = armBotPath.points.connections.targets || []; - const alreadyConnectedToStatic = armBotConnections.some(target => { - const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID); - return targetPath?.type === 'StaticMachine'; - }); - - if (alreadyConnectedToStatic) { - console.log("ArmBot can only connect to one StaticMachine"); - return; - } - - const staticConnections = staticPath.points.connections.targets.length; - if (staticConnections >= 1) { - console.log("StaticMachine can only have one connection"); - return; - } - } - - // Prevent ArmBot ↔ ArmBot - if (firstPath?.type === 'ArmBot' && secondPath?.type === 'ArmBot') { - console.log("Cannot connect two ArmBots together"); - return; - } - - // If one is ArmBot, ensure the other is StaticMachine or Conveyor - if (firstPath?.type === 'ArmBot' || secondPath?.type === 'ArmBot') { - const otherType = firstPath?.type === 'ArmBot' ? secondPath?.type : firstPath?.type; - if (otherType !== 'StaticMachine' && otherType !== 'Conveyor') { - console.log("ArmBot can only connect to Conveyors or one StaticMachine"); - return; - } - } - - // At least one must be start/end point - if (!firstSelected.isCorner && !isStartOrEnd) { - console.log("At least one of the selected spheres must be a start or end point."); - return; - } - - // All checks passed - make the connection - handleAddConnection(firstSelected.modelUUID, firstSelected.sphereUUID, modelUUID, sphereUUID); - } else { - // First selection - just store it - setFirstSelected({ modelUUID, sphereUUID, position: worldPosition, isCorner: isStartOrEnd }); - setIsConnecting(true); - } - } - } - } else { - // Clicked outside - cancel connection - setFirstSelected(null); - setCurrentLine(null); - setIsConnecting(false); - } - }; - - if (activeModule === 'simulation' && !deleteTool) { - canvasElement.addEventListener("mousedown", onMouseDown); - canvasElement.addEventListener("mouseup", onMouseUp); - canvasElement.addEventListener("mousemove", onMouseMove); - canvasElement.addEventListener("contextmenu", onContextMenu); - } else { - setFirstSelected(null); - setCurrentLine(null); - setIsConnecting(false); - } - - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - canvasElement.removeEventListener("contextmenu", onContextMenu); - }; - }, [camera, scene, raycaster, firstSelected, simulationStates, deleteTool]); - - useFrame(() => { - Object.values(groupRefs.current).forEach((group) => { - if (group) { - const distance = new THREE.Vector3(...group.position.toArray()).distanceTo(camera.position); - group.visible = ((distance <= renderDistance) && !isPlaying); - } - }); + useFrame(() => { + Object.values(groupRefs.current).forEach((group) => { + if (group) { + const distance = new THREE.Vector3( + ...group.position.toArray() + ).distanceTo(camera.position); + group.visible = distance <= renderDistance && !isPlaying; + } }); + }); - useFrame(() => { - if (firstSelected) { - 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") - ); - - let point: THREE.Vector3 | null = null; - let snappedSphere: { sphereUUID: string, position: THREE.Vector3, modelUUID: string, isCorner: boolean } | null = null; - let isInvalidConnection = false; - - if (intersects.length > 0) { - point = intersects[0].point; - if (point.y < 0.05) { - point = new THREE.Vector3(point.x, 0.05, point.z); - } - } - - const sphereIntersects = raycaster.intersectObjects(pathsGroupRef.current.children, true).filter((obj) => obj.object.name.includes("events-sphere")); - - if (sphereIntersects.length > 0) { - const sphere = sphereIntersects[0].object; - const sphereUUID = sphere.uuid; - const spherePosition = new THREE.Vector3(); - sphere.getWorldPosition(spherePosition); - const pathData = sphere.userData.path; - const modelUUID = pathData.modeluuid; - - const firstPath = simulationStates.find(p => p.modeluuid === firstSelected.modelUUID); - const secondPath = simulationStates.find(p => p.modeluuid === modelUUID); - const isVehicleToVehicle = firstPath?.type === 'Vehicle' && secondPath?.type === 'Vehicle'; - - // Inside the useFrame hook, where we check for snapped spheres: - const isConnectable = ( - pathData.type === 'Vehicle' || - pathData.type === 'ArmBot' || - (pathData.points.length > 0 && ( - sphereUUID === pathData.points[0].uuid || - sphereUUID === pathData.points[pathData.points.length - 1].uuid || - (pathData.type === 'Conveyor' && firstPath?.type === 'ArmBot') // Allow ArmBot to connect to middle points - )) - ) && - !isVehicleToVehicle && - !( - firstPath?.type === 'Conveyor' && - pathData.type === 'Conveyor' && - !firstSelected.isCorner - ); - - // Check for duplicate connection (regardless of path type) - const isDuplicateConnection = simulationStates.some(path => { - if (path.modeluuid === firstSelected.modelUUID) { - if (path.type === 'Conveyor') { - const point = path.points.find(p => p.uuid === firstSelected.sphereUUID); - return point?.connections.targets.some(t => - t.modelUUID === modelUUID && t.pointUUID === sphereUUID - ); - } else if (path.type === 'Vehicle') { - return path.points.connections.targets.some(t => - t.modelUUID === modelUUID && t.pointUUID === sphereUUID - ); - } - } - return false; - }); - - // For non-Vehicle paths, check if already connected - const isNonVehicleAlreadyConnected = pathData.type !== 'Vehicle' && - simulationStates.some(path => { - if (path.type === 'Conveyor') { - return path.points.some(point => - point.uuid === sphereUUID && - point.connections.targets.length > 0 - ); - } - return false; - }); - - // Check vehicle connection rules - const isVehicleAtMaxConnections = pathData.type === 'Vehicle' && - pathData.points.connections.targets.length >= 1; - const isVehicleConnectingToNonConveyor = - (firstPath?.type === 'Vehicle' && secondPath?.type !== 'Conveyor') || - (secondPath?.type === 'Vehicle' && firstPath?.type !== 'Conveyor'); - - // Check if StaticMachine is connecting to non-ArmBot - const isStaticMachineToNonArmBot = - (firstPath?.type === 'StaticMachine' && secondPath?.type !== 'ArmBot') || - (secondPath?.type === 'StaticMachine' && firstPath?.type !== 'ArmBot'); - - // Check if StaticMachine already has a connection - const isStaticMachineAtMaxConnections = - (firstPath?.type === 'StaticMachine' && firstPath.points.connections.targets.length >= 1) || - (secondPath?.type === 'StaticMachine' && secondPath.points.connections.targets.length >= 1); - - // Check if ArmBot is connecting to StaticMachine - const isArmBotToStaticMachine = - (firstPath?.type === 'ArmBot' && secondPath?.type === 'StaticMachine') || - (secondPath?.type === 'ArmBot' && firstPath?.type === 'StaticMachine'); - - // Prevent multiple StaticMachine connections to ArmBot - let isArmBotAlreadyConnectedToStatic = false; - if (isArmBotToStaticMachine) { - const armBotPath = firstPath?.type === 'ArmBot' ? firstPath : secondPath; - isArmBotAlreadyConnectedToStatic = armBotPath.points.connections.targets.some(target => { - const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID); - return targetPath?.type === 'StaticMachine'; - }); - } - - // Prevent ArmBot to ArmBot - const isArmBotToArmBot = firstPath?.type === 'ArmBot' && secondPath?.type === 'ArmBot'; - - // If ArmBot is involved, other must be Conveyor or StaticMachine - const isArmBotToInvalidType = (firstPath?.type === 'ArmBot' || secondPath?.type === 'ArmBot') && - !(firstPath?.type === 'Conveyor' || firstPath?.type === 'StaticMachine' || - secondPath?.type === 'Conveyor' || secondPath?.type === 'StaticMachine'); - - if ( - !isDuplicateConnection && - !isVehicleToVehicle && - !isNonVehicleAlreadyConnected && - !isVehicleAtMaxConnections && - !isVehicleConnectingToNonConveyor && - !isStaticMachineToNonArmBot && - !isStaticMachineAtMaxConnections && - !isArmBotToArmBot && - !isArmBotToInvalidType && - !isArmBotAlreadyConnectedToStatic && - firstSelected.sphereUUID !== sphereUUID && - firstSelected.modelUUID !== modelUUID && - (firstSelected.isCorner || isConnectable) && - !(firstPath?.type === 'Conveyor' && - pathData.type === 'Conveyor' && - !(firstSelected.isCorner && isConnectable)) - ) { - snappedSphere = { - sphereUUID, - position: spherePosition, - modelUUID, - isCorner: isConnectable - }; - } else { - isInvalidConnection = true; - } - - } - - if (snappedSphere) { - point = snappedSphere.position; - } - - if (point) { - const distance = firstSelected.position.distanceTo(point); - const heightFactor = Math.max(0.5, distance * 0.2); - const midPoint = new THREE.Vector3( - (firstSelected.position.x + point.x) / 2, - Math.max(firstSelected.position.y, point.y) + heightFactor, - (firstSelected.position.z + point.z) / 2 - ); - - setCurrentLine({ - start: firstSelected.position, - end: point, - mid: midPoint, - }); - - if (sphereIntersects.length > 0) { - setHelperLineColor(isInvalidConnection ? 'red' : '#6cf542'); - } else { - setHelperLineColor('yellow'); - } - } else { - setCurrentLine(null); - setIsConnecting(false); - } - } else { - setCurrentLine(null); - setIsConnecting(false); - } - }); - - const removeConnections = (connection1: { model: string; point: string }, connection2: { model: string; point: string }) => { - const updatedStates = simulationStates.map(state => { - // Handle Conveyor (which has multiple points) - if (state.type === 'Conveyor') { - const updatedConveyor: Types.ConveyorEventsSchema = { - ...state, - points: state.points.map(point => { - // Check if this point is either connection1 or connection2 - if ((state.modeluuid === connection1.model && point.uuid === connection1.point) || - (state.modeluuid === connection2.model && point.uuid === connection2.point)) { - - return { - ...point, - connections: { - ...point.connections, - targets: point.connections.targets.filter(target => { - // Remove the target that matches the other connection - return !( - (target.modelUUID === connection1.model && target.pointUUID === connection1.point) || - (target.modelUUID === connection2.model && target.pointUUID === connection2.point) - ); - }) - } - }; - } - return point; - }) - }; - return updatedConveyor; - } - // Handle Vehicle - else if (state.type === 'Vehicle') { - if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) || - (state.modeluuid === connection2.model && state.points.uuid === connection2.point)) { - - const updatedVehicle: Types.VehicleEventsSchema = { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter(target => { - return !( - (target.modelUUID === connection1.model && target.pointUUID === connection1.point) || - (target.modelUUID === connection2.model && target.pointUUID === connection2.point) - ); - }) - }, - // Ensure all required Vehicle point properties are included - speed: state.points.speed, - actions: state.points.actions - } - }; - return updatedVehicle; - } - } - // Handle StaticMachine - else if (state.type === 'StaticMachine') { - if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) || - (state.modeluuid === connection2.model && state.points.uuid === connection2.point)) { - - const updatedStaticMachine: Types.StaticMachineEventsSchema = { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter(target => { - return !( - (target.modelUUID === connection1.model && target.pointUUID === connection1.point) || - (target.modelUUID === connection2.model && target.pointUUID === connection2.point) - ); - }) - }, - // Ensure all required StaticMachine point properties are included - actions: state.points.actions, - triggers: state.points.triggers - } - }; - return updatedStaticMachine; - } - } - // Handle ArmBot - else if (state.type === 'ArmBot') { - if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) || - (state.modeluuid === connection2.model && state.points.uuid === connection2.point)) { - - const updatedArmBot: Types.ArmBotEventsSchema = { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter(target => { - return !( - (target.modelUUID === connection1.model && target.pointUUID === connection1.point) || - (target.modelUUID === connection2.model && target.pointUUID === connection2.point) - ); - }) - }, - actions: { - ...state.points.actions, - processes: state.points.actions.processes?.filter(process => { - return !( - process.startPoint === connection1.point || - process.endPoint === connection1.point || - process.startPoint === connection2.point || - process.endPoint === connection2.point - ); - }) || [] - }, - triggers: state.points.triggers - } - }; - return updatedArmBot; - } - } - return state; - }); - - const updatedPaths = updatedStates.filter(state => - state.modeluuid === connection1.model || state.modeluuid === connection2.model + useFrame(() => { + if (firstSelected) { + 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") ); - updateBackend(updatedPaths); + let point: THREE.Vector3 | null = null; + let snappedSphere: { + sphereUUID: string; + position: THREE.Vector3; + modelUUID: string; + isCorner: boolean; + } | null = null; + let isInvalidConnection = false; - setSimulationStates(updatedStates); - }; + if (intersects.length > 0) { + point = intersects[0].point; + if (point.y < 0.05) { + point = new THREE.Vector3(point.x, 0.05, point.z); + } + } + const sphereIntersects = raycaster + .intersectObjects(pathsGroupRef.current.children, true) + .filter((obj) => obj.object.name.includes("events-sphere")); - return ( - - {simulationStates.flatMap(path => { - if (path.type === 'Conveyor') { - return path.points.flatMap(point => - point.connections.targets.map((target, index) => { - const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID); - if (targetPath?.type !== 'Conveyor' && targetPath?.type !== 'ArmBot') return null; + if (sphereIntersects.length > 0) { + const sphere = sphereIntersects[0].object; + const sphereUUID = sphere.uuid; + const spherePosition = new THREE.Vector3(); + sphere.getWorldPosition(spherePosition); + const pathData = sphere.userData.path; + const modelUUID = pathData.modeluuid; - const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', point.uuid); - const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID); + const firstPath = simulationStates.find( + (p) => p.modeluuid === firstSelected.modelUUID + ); + const secondPath = simulationStates.find( + (p) => p.modeluuid === modelUUID + ); + const isVehicleToVehicle = + firstPath?.type === "Vehicle" && secondPath?.type === "Vehicle"; - if (fromSphere && toSphere) { - const fromWorldPosition = new THREE.Vector3(); - const toWorldPosition = new THREE.Vector3(); - fromSphere.getWorldPosition(fromWorldPosition); - toSphere.getWorldPosition(toWorldPosition); + // Inside the useFrame hook, where we check for snapped spheres: + const isConnectable = + (pathData.type === "Vehicle" || + pathData.type === "ArmBot" || + (pathData.points.length > 0 && + (sphereUUID === pathData.points[0].uuid || + sphereUUID === + pathData.points[pathData.points.length - 1].uuid || + (pathData.type === "Conveyor" && + firstPath?.type === "ArmBot")))) && // Allow ArmBot to connect to middle points + !isVehicleToVehicle && + !( + firstPath?.type === "Conveyor" && + pathData.type === "Conveyor" && + !firstSelected.isCorner + ); - 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); + // Check for duplicate connection (regardless of path type) + const isDuplicateConnection = simulationStates.some((path) => { + if (path.modeluuid === firstSelected.modelUUID) { + if (path.type === "Conveyor") { + const point = path.points.find( + (p) => p.uuid === firstSelected.sphereUUID + ); + return point?.connections.targets.some( + (t) => t.modelUUID === modelUUID && t.pointUUID === sphereUUID + ); + } else if (path.type === "Vehicle") { + return path.points.connections.targets.some( + (t) => t.modelUUID === modelUUID && t.pointUUID === sphereUUID + ); + } + } + return false; + }); - return ( - (groupRefs.current[`${point.uuid}-${target.pointUUID}-${index}`] = el!)} - start={fromWorldPosition.toArray()} - end={toWorldPosition.toArray()} - mid={midPoint.toArray()} - color={ - deleteTool && hoveredLineKey === `${point.uuid}-${target.pointUUID}-${index}` - ? 'red' - : targetPath?.type === 'ArmBot' - ? '#42a5f5' - : 'white' - } - lineWidth={4} - dashed={(deleteTool && hoveredLineKey === `${point.uuid}-${target.pointUUID}-${index}`) ? false : true} - dashSize={0.75} - dashScale={20} - onPointerOver={() => setHoveredLineKey(`${point.uuid}-${target.pointUUID}-${index}`)} - onPointerOut={() => setHoveredLineKey(null)} - onClick={() => { - if (deleteTool) { + // For non-Vehicle paths, check if already connected + const isNonVehicleAlreadyConnected = + pathData.type !== "Vehicle" && + simulationStates.some((path) => { + if (path.type === "Conveyor") { + return path.points.some( + (point) => + point.uuid === sphereUUID && + point.connections.targets.length > 0 + ); + } + return false; + }); - const connection1 = { model: path.modeluuid, point: point.uuid } - const connection2 = { model: target.modelUUID, point: target.pointUUID } + // Check vehicle connection rules + const isVehicleAtMaxConnections = + pathData.type === "Vehicle" && + pathData.points.connections.targets.length >= 1; + const isVehicleConnectingToNonConveyor = + (firstPath?.type === "Vehicle" && secondPath?.type !== "Conveyor") || + (secondPath?.type === "Vehicle" && firstPath?.type !== "Conveyor"); - removeConnections(connection1, connection2) + // Check if StaticMachine is connecting to non-ArmBot + const isStaticMachineToNonArmBot = + (firstPath?.type === "StaticMachine" && + secondPath?.type !== "ArmBot") || + (secondPath?.type === "StaticMachine" && + firstPath?.type !== "ArmBot"); - } - }} - userData={target} - /> - ); - } - return null; - }) + // Check if StaticMachine already has a connection + const isStaticMachineAtMaxConnections = + (firstPath?.type === "StaticMachine" && + firstPath.points.connections.targets.length >= 1) || + (secondPath?.type === "StaticMachine" && + secondPath.points.connections.targets.length >= 1); + + // Check if ArmBot is connecting to StaticMachine + const isArmBotToStaticMachine = + (firstPath?.type === "ArmBot" && + secondPath?.type === "StaticMachine") || + (secondPath?.type === "ArmBot" && + firstPath?.type === "StaticMachine"); + + // Prevent multiple StaticMachine connections to ArmBot + let isArmBotAlreadyConnectedToStatic = false; + if (isArmBotToStaticMachine) { + const armBotPath = + firstPath?.type === "ArmBot" ? firstPath : secondPath; + isArmBotAlreadyConnectedToStatic = + armBotPath.points.connections.targets.some((target) => { + const targetPath = simulationStates.find( + (p) => p.modeluuid === target.modelUUID + ); + return targetPath?.type === "StaticMachine"; + }); + } + + // Prevent ArmBot to ArmBot + const isArmBotToArmBot = + firstPath?.type === "ArmBot" && secondPath?.type === "ArmBot"; + + // If ArmBot is involved, other must be Conveyor or StaticMachine + const isArmBotToInvalidType = + (firstPath?.type === "ArmBot" || secondPath?.type === "ArmBot") && + !( + firstPath?.type === "Conveyor" || + firstPath?.type === "StaticMachine" || + secondPath?.type === "Conveyor" || + secondPath?.type === "StaticMachine" + ); + + if ( + !isDuplicateConnection && + !isVehicleToVehicle && + !isNonVehicleAlreadyConnected && + !isVehicleAtMaxConnections && + !isVehicleConnectingToNonConveyor && + !isStaticMachineToNonArmBot && + !isStaticMachineAtMaxConnections && + !isArmBotToArmBot && + !isArmBotToInvalidType && + !isArmBotAlreadyConnectedToStatic && + firstSelected.sphereUUID !== sphereUUID && + firstSelected.modelUUID !== modelUUID && + (firstSelected.isCorner || isConnectable) && + !( + firstPath?.type === "Conveyor" && + pathData.type === "Conveyor" && + !(firstSelected.isCorner && isConnectable) + ) + ) { + snappedSphere = { + sphereUUID, + position: spherePosition, + modelUUID, + isCorner: isConnectable, + }; + } else { + isInvalidConnection = true; + } + } + + if (snappedSphere) { + point = snappedSphere.position; + } + + if (point) { + const distance = firstSelected.position.distanceTo(point); + const heightFactor = Math.max(0.5, distance * 0.2); + const midPoint = new THREE.Vector3( + (firstSelected.position.x + point.x) / 2, + Math.max(firstSelected.position.y, point.y) + heightFactor, + (firstSelected.position.z + point.z) / 2 + ); + + setCurrentLine({ + start: firstSelected.position, + end: point, + mid: midPoint, + }); + + if (sphereIntersects.length > 0) { + setHelperLineColor(isInvalidConnection ? "red" : "#6cf542"); + } else { + setHelperLineColor("yellow"); + } + } else { + setCurrentLine(null); + setIsConnecting(false); + } + } else { + setCurrentLine(null); + setIsConnecting(false); + } + }); + + const removeConnections = ( + connection1: { model: string; point: string }, + connection2: { model: string; point: string } + ) => { + const updatedStates = simulationStates.map((state) => { + // Handle Conveyor (which has multiple points) + if (state.type === "Conveyor") { + const updatedConveyor: Types.ConveyorEventsSchema = { + ...state, + points: state.points.map((point) => { + // Check if this point is either connection1 or connection2 + if ( + (state.modeluuid === connection1.model && + point.uuid === connection1.point) || + (state.modeluuid === connection2.model && + point.uuid === connection2.point) + ) { + return { + ...point, + connections: { + ...point.connections, + targets: point.connections.targets.filter((target) => { + // Remove the target that matches the other connection + return !( + (target.modelUUID === connection1.model && + target.pointUUID === connection1.point) || + (target.modelUUID === connection2.model && + target.pointUUID === connection2.point) ); - } + }), + }, + }; + } + return point; + }), + }; + return updatedConveyor; + } + // Handle Vehicle + else if (state.type === "Vehicle") { + if ( + (state.modeluuid === connection1.model && + state.points.uuid === connection1.point) || + (state.modeluuid === connection2.model && + state.points.uuid === connection2.point) + ) { + const updatedVehicle: Types.VehicleEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter((target) => { + return !( + (target.modelUUID === connection1.model && + target.pointUUID === connection1.point) || + (target.modelUUID === connection2.model && + target.pointUUID === connection2.point) + ); + }), + }, + // Ensure all required Vehicle point properties are included + speed: state.points.speed, + actions: state.points.actions, + }, + }; + return updatedVehicle; + } + } + // Handle StaticMachine + else if (state.type === "StaticMachine") { + if ( + (state.modeluuid === connection1.model && + state.points.uuid === connection1.point) || + (state.modeluuid === connection2.model && + state.points.uuid === connection2.point) + ) { + const updatedStaticMachine: Types.StaticMachineEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter((target) => { + return !( + (target.modelUUID === connection1.model && + target.pointUUID === connection1.point) || + (target.modelUUID === connection2.model && + target.pointUUID === connection2.point) + ); + }), + }, + // Ensure all required StaticMachine point properties are included + actions: state.points.actions, + triggers: state.points.triggers, + }, + }; + return updatedStaticMachine; + } + } + // Handle ArmBot + else if (state.type === "ArmBot") { + if ( + (state.modeluuid === connection1.model && + state.points.uuid === connection1.point) || + (state.modeluuid === connection2.model && + state.points.uuid === connection2.point) + ) { + const updatedArmBot: Types.ArmBotEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter((target) => { + return !( + (target.modelUUID === connection1.model && + target.pointUUID === connection1.point) || + (target.modelUUID === connection2.model && + target.pointUUID === connection2.point) + ); + }), + }, + actions: { + ...state.points.actions, + processes: + state.points.actions.processes?.filter((process) => { + return !( + process.startPoint === connection1.point || + process.endPoint === connection1.point || + process.startPoint === connection2.point || + process.endPoint === connection2.point + ); + }) || [], + }, + triggers: state.points.triggers, + }, + }; + return updatedArmBot; + } + } + return state; + }); - if (path.type === 'Vehicle') { - return path.points.connections.targets.map((target, index) => { - const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', path.points.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 ( - (groupRefs.current[`${path.points.uuid}-${target.pointUUID}-${index}`] = el!)} - start={fromWorldPosition.toArray()} - end={toWorldPosition.toArray()} - mid={midPoint.toArray()} - color={ - deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}` - ? 'red' - : 'orange' - } - lineWidth={4} - dashed={(deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}`) ? false : true} - dashSize={0.75} - dashScale={20} - onPointerOver={() => setHoveredLineKey(`${path.points.uuid}-${target.pointUUID}-${index}`)} - onPointerOut={() => setHoveredLineKey(null)} - onClick={() => { - if (deleteTool) { - - const connection1 = { model: path.modeluuid, point: path.points.uuid } - const connection2 = { model: target.modelUUID, point: target.pointUUID } - - removeConnections(connection1, connection2) - } - }} - userData={target} - /> - ); - } - return null; - }); - } - - if (path.type === 'StaticMachine') { - return path.points.connections.targets.map((target, index) => { - const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID); - if (targetPath?.type !== 'ArmBot') return null; - - const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', path.points.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 ( - (groupRefs.current[`${path.points.uuid}-${target.pointUUID}-${index}`] = el!)} - start={fromWorldPosition.toArray()} - end={toWorldPosition.toArray()} - mid={midPoint.toArray()} - color={ - deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}` - ? 'red' - : '#42a5f5' - } - lineWidth={4} - dashed={(deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}`) ? false : true} - dashSize={0.75} - dashScale={20} - onPointerOver={() => setHoveredLineKey(`${path.points.uuid}-${target.pointUUID}-${index}`)} - onPointerOut={() => setHoveredLineKey(null)} - onClick={() => { - if (deleteTool) { - - const connection1 = { model: path.modeluuid, point: path.points.uuid } - const connection2 = { model: target.modelUUID, point: target.pointUUID } - - removeConnections(connection1, connection2) - - } - }} - userData={target} - /> - ); - } - return null; - }); - } - - return []; - })} - - {currentLine && ( - - )} - + const updatedPaths = updatedStates.filter( + (state) => + state.modeluuid === connection1.model || + state.modeluuid === connection2.model ); + console.log("updatedPaths: ", updatedPaths); + updateBackend(updatedPaths); + + setSimulationStates(updatedStates); + }; + + return ( + + {simulationStates.flatMap((path) => { + if (path.type === "Conveyor") { + return path.points.flatMap((point) => + point.connections.targets.map((target, index) => { + const targetPath = simulationStates.find( + (p) => p.modeluuid === target.modelUUID + ); + if ( + targetPath?.type !== "Conveyor" && + targetPath?.type !== "ArmBot" + ) + 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 ( + + (groupRefs.current[ + `${point.uuid}-${target.pointUUID}-${index}` + ] = el!) + } + start={fromWorldPosition.toArray()} + end={toWorldPosition.toArray()} + mid={midPoint.toArray()} + color={ + deleteTool && + hoveredLineKey === + `${point.uuid}-${target.pointUUID}-${index}` + ? "red" + : targetPath?.type === "ArmBot" + ? "#42a5f5" + : "white" + } + lineWidth={4} + dashed={ + deleteTool && + hoveredLineKey === + `${point.uuid}-${target.pointUUID}-${index}` + ? false + : true + } + dashSize={0.75} + dashScale={20} + onPointerOver={() => + setHoveredLineKey( + `${point.uuid}-${target.pointUUID}-${index}` + ) + } + onPointerOut={() => setHoveredLineKey(null)} + onClick={() => { + if (deleteTool) { + const connection1 = { + model: path.modeluuid, + point: point.uuid, + }; + const connection2 = { + model: target.modelUUID, + point: target.pointUUID, + }; + + removeConnections(connection1, connection2); + } + }} + userData={target} + /> + ); + } + return null; + }) + ); + } + + if (path.type === "Vehicle") { + return path.points.connections.targets.map((target, index) => { + const fromSphere = pathsGroupRef.current?.getObjectByProperty( + "uuid", + path.points.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 ( + + (groupRefs.current[ + `${path.points.uuid}-${target.pointUUID}-${index}` + ] = el!) + } + start={fromWorldPosition.toArray()} + end={toWorldPosition.toArray()} + mid={midPoint.toArray()} + color={ + deleteTool && + hoveredLineKey === + `${path.points.uuid}-${target.pointUUID}-${index}` + ? "red" + : "orange" + } + lineWidth={4} + dashed={ + deleteTool && + hoveredLineKey === + `${path.points.uuid}-${target.pointUUID}-${index}` + ? false + : true + } + dashSize={0.75} + dashScale={20} + onPointerOver={() => + setHoveredLineKey( + `${path.points.uuid}-${target.pointUUID}-${index}` + ) + } + onPointerOut={() => setHoveredLineKey(null)} + onClick={() => { + if (deleteTool) { + const connection1 = { + model: path.modeluuid, + point: path.points.uuid, + }; + const connection2 = { + model: target.modelUUID, + point: target.pointUUID, + }; + + removeConnections(connection1, connection2); + } + }} + userData={target} + /> + ); + } + return null; + }); + } + + if (path.type === "StaticMachine") { + return path.points.connections.targets.map((target, index) => { + const targetPath = simulationStates.find( + (p) => p.modeluuid === target.modelUUID + ); + if (targetPath?.type !== "ArmBot") return null; + + const fromSphere = pathsGroupRef.current?.getObjectByProperty( + "uuid", + path.points.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 ( + + (groupRefs.current[ + `${path.points.uuid}-${target.pointUUID}-${index}` + ] = el!) + } + start={fromWorldPosition.toArray()} + end={toWorldPosition.toArray()} + mid={midPoint.toArray()} + color={ + deleteTool && + hoveredLineKey === + `${path.points.uuid}-${target.pointUUID}-${index}` + ? "red" + : "#42a5f5" + } + lineWidth={4} + dashed={ + deleteTool && + hoveredLineKey === + `${path.points.uuid}-${target.pointUUID}-${index}` + ? false + : true + } + dashSize={0.75} + dashScale={20} + onPointerOver={() => + setHoveredLineKey( + `${path.points.uuid}-${target.pointUUID}-${index}` + ) + } + onPointerOut={() => setHoveredLineKey(null)} + onClick={() => { + if (deleteTool) { + const connection1 = { + model: path.modeluuid, + point: path.points.uuid, + }; + const connection2 = { + model: target.modelUUID, + point: target.pointUUID, + }; + + removeConnections(connection1, connection2); + } + }} + userData={target} + /> + ); + } + return null; + }); + } + + return []; + })} + + {currentLine && ( + + )} + + ); } -export default PathConnector; \ No newline at end of file +export default PathConnector; From 344650730725600b882ee4fa33885dfe3fcfa0fa Mon Sep 17 00:00:00 2001 From: Poovizhi99 Date: Tue, 15 Apr 2025 10:05:53 +0530 Subject: [PATCH 2/5] removed targets based on condition for armbot --- .../controls/selection/selectionControls.tsx | 1300 +++++++++-------- 1 file changed, 712 insertions(+), 588 deletions(-) diff --git a/app/src/modules/scene/controls/selection/selectionControls.tsx b/app/src/modules/scene/controls/selection/selectionControls.tsx index d1eed1e..80a7518 100644 --- a/app/src/modules/scene/controls/selection/selectionControls.tsx +++ b/app/src/modules/scene/controls/selection/selectionControls.tsx @@ -3,7 +3,13 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { SelectionBox } from "three/examples/jsm/interactive/SelectionBox"; import { SelectionHelper } from "./selectionHelper"; import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore, useToggleView, } from "../../../../store/store"; +import { + useFloorItems, + useSelectedAssets, + useSimulationStates, + useSocketStore, + useToggleView, +} from "../../../../store/store"; import BoundingBox from "./boundingBoxHelper"; import { toast } from "react-toastify"; // import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; @@ -17,622 +23,740 @@ import RotateControls from "./rotateControls"; import useModuleStore from "../../../../store/useModuleStore"; const SelectionControls: React.FC = () => { - const { camera, controls, gl, scene, pointer } = useThree(); - const itemsGroupRef = useRef(undefined); - const selectionGroup = useRef() as Types.RefGroup; - const { toggleView } = useToggleView(); - const { simulationStates, setSimulationStates } = useSimulationStates(); - const { selectedAssets, setSelectedAssets } = useSelectedAssets(); - const [movedObjects, setMovedObjects] = useState([]); - const [rotatedObjects, setRotatedObjects] = useState([]); - const [copiedObjects, setCopiedObjects] = useState([]); - const [pastedObjects, setpastedObjects] = useState([]); - const [duplicatedObjects, setDuplicatedObjects] = useState([]); - const boundingBoxRef = useRef(); - const { floorItems, setFloorItems } = useFloorItems(); - const { activeModule } = useModuleStore(); - const { socket } = useSocketStore(); - const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]); + const { camera, controls, gl, scene, pointer } = useThree(); + const itemsGroupRef = useRef(undefined); + const selectionGroup = useRef() as Types.RefGroup; + const { toggleView } = useToggleView(); + const { simulationStates, setSimulationStates } = useSimulationStates(); + const { selectedAssets, setSelectedAssets } = useSelectedAssets(); + const [movedObjects, setMovedObjects] = useState([]); + const [rotatedObjects, setRotatedObjects] = useState([]); + const [copiedObjects, setCopiedObjects] = useState([]); + const [pastedObjects, setpastedObjects] = useState([]); + const [duplicatedObjects, setDuplicatedObjects] = useState( + [] + ); + const boundingBoxRef = useRef(); + const { floorItems, setFloorItems } = useFloorItems(); + const { activeModule } = useModuleStore(); + const { socket } = useSocketStore(); + const selectionBox = useMemo( + () => new SelectionBox(camera, scene), + [camera, scene] + ); - useEffect(() => { - if (!camera || !scene || toggleView) return; + useEffect(() => { + if (!camera || !scene || toggleView) return; - const canvasElement = gl.domElement; - canvasElement.tabIndex = 0; + const canvasElement = gl.domElement; + canvasElement.tabIndex = 0; - const itemsGroup: any = scene.getObjectByName("itemsGroup"); - itemsGroupRef.current = itemsGroup; + const itemsGroup: any = scene.getObjectByName("itemsGroup"); + itemsGroupRef.current = itemsGroup; - let isSelecting = false; - let isRightClick = false; - let rightClickMoved = false; - let isCtrlSelecting = false; + let isSelecting = false; + let isRightClick = false; + let rightClickMoved = false; + let isCtrlSelecting = false; - const helper = new SelectionHelper(gl); + const helper = new SelectionHelper(gl); - if (!itemsGroup) { - toast.warn("itemsGroup not found in the scene."); - return; + if (!itemsGroup) { + toast.warn("itemsGroup not found in the scene."); + return; + } + + const onPointerDown = (event: PointerEvent) => { + if (event.button === 2) { + isRightClick = true; + rightClickMoved = false; + } else if (event.button === 0) { + isSelecting = false; + isCtrlSelecting = event.ctrlKey; + if (event.ctrlKey && duplicatedObjects.length === 0) { + if (controls) (controls as any).enabled = false; + selectionBox.startPoint.set(pointer.x, pointer.y, 0); } + } + }; - const onPointerDown = (event: PointerEvent) => { - if (event.button === 2) { - isRightClick = true; - rightClickMoved = false; - } else if (event.button === 0) { - isSelecting = false; - isCtrlSelecting = event.ctrlKey; - if (event.ctrlKey && duplicatedObjects.length === 0) { - if (controls) (controls as any).enabled = false; - selectionBox.startPoint.set(pointer.x, pointer.y, 0); - } - } - }; + const onPointerMove = (event: PointerEvent) => { + if (isRightClick) { + rightClickMoved = true; + } + isSelecting = true; + if ( + helper.isDown && + event.ctrlKey && + duplicatedObjects.length === 0 && + isCtrlSelecting + ) { + selectionBox.endPoint.set(pointer.x, pointer.y, 0); + } + }; - const onPointerMove = (event: PointerEvent) => { - if (isRightClick) { - rightClickMoved = true; - } - isSelecting = true; - if (helper.isDown && event.ctrlKey && duplicatedObjects.length === 0 && isCtrlSelecting) { - selectionBox.endPoint.set(pointer.x, pointer.y, 0); - } - }; - - const onPointerUp = (event: PointerEvent) => { - if (event.button === 2) { - isRightClick = false; - if (!rightClickMoved) { - clearSelection(); - } - return; - } - - if (isSelecting && isCtrlSelecting) { - isCtrlSelecting = false; - isSelecting = false; - if (event.ctrlKey && duplicatedObjects.length === 0) { - selectAssets(); - } - } else if (!isSelecting && selectedAssets.length > 0 && ((pastedObjects.length === 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) || event.button !== 0)) { - clearSelection(); - helper.enabled = true; - isCtrlSelecting = false; - } - }; - - const onKeyDown = (event: KeyboardEvent) => { - if (movedObjects.length > 0 || rotatedObjects.length > 0) return; - if (event.key.toLowerCase() === "escape") { - event.preventDefault(); - clearSelection(); - } - if (event.key.toLowerCase() === "delete") { - event.preventDefault(); - deleteSelection(); - } - }; - - const onContextMenu = (event: MouseEvent) => { - event.preventDefault(); - if (!rightClickMoved) { - clearSelection(); - } - }; - - if (!toggleView && activeModule === "builder") { - helper.enabled = true; - if (duplicatedObjects.length === 0 && pastedObjects.length === 0) { - canvasElement.addEventListener("pointerdown", onPointerDown); - canvasElement.addEventListener("pointermove", onPointerMove); - canvasElement.addEventListener("pointerup", onPointerUp); - } else { - helper.enabled = false; - helper.dispose(); - } - canvasElement.addEventListener("contextmenu", onContextMenu); - canvasElement.addEventListener("keydown", onKeyDown); - } else { - helper.enabled = false; - helper.dispose(); + const onPointerUp = (event: PointerEvent) => { + if (event.button === 2) { + isRightClick = false; + if (!rightClickMoved) { + clearSelection(); } + return; + } - return () => { - canvasElement.removeEventListener("pointerdown", onPointerDown); - canvasElement.removeEventListener("pointermove", onPointerMove); - canvasElement.removeEventListener("contextmenu", onContextMenu); - canvasElement.removeEventListener("pointerup", onPointerUp); - canvasElement.removeEventListener("keydown", onKeyDown); - helper.enabled = false; - helper.dispose(); - }; - }, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, floorItems, rotatedObjects, activeModule,]); - - useEffect(() => { - if (activeModule !== "builder") { - clearSelection(); + if (isSelecting && isCtrlSelecting) { + isCtrlSelecting = false; + isSelecting = false; + if (event.ctrlKey && duplicatedObjects.length === 0) { + selectAssets(); } - }, [activeModule]); + } else if ( + !isSelecting && + selectedAssets.length > 0 && + ((pastedObjects.length === 0 && + duplicatedObjects.length === 0 && + movedObjects.length === 0 && + rotatedObjects.length === 0) || + event.button !== 0) + ) { + clearSelection(); + helper.enabled = true; + isCtrlSelecting = false; + } + }; - useFrame(() => { - if (pastedObjects.length === 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { - selectionGroup.current.position.set(0, 0, 0); + const onKeyDown = (event: KeyboardEvent) => { + if (movedObjects.length > 0 || rotatedObjects.length > 0) return; + if (event.key.toLowerCase() === "escape") { + event.preventDefault(); + clearSelection(); + } + if (event.key.toLowerCase() === "delete") { + event.preventDefault(); + deleteSelection(); + } + }; + + const onContextMenu = (event: MouseEvent) => { + event.preventDefault(); + if (!rightClickMoved) { + clearSelection(); + } + }; + + if (!toggleView && activeModule === "builder") { + helper.enabled = true; + if (duplicatedObjects.length === 0 && pastedObjects.length === 0) { + canvasElement.addEventListener("pointerdown", onPointerDown); + canvasElement.addEventListener("pointermove", onPointerMove); + canvasElement.addEventListener("pointerup", onPointerUp); + } else { + helper.enabled = false; + helper.dispose(); + } + canvasElement.addEventListener("contextmenu", onContextMenu); + canvasElement.addEventListener("keydown", onKeyDown); + } else { + helper.enabled = false; + helper.dispose(); + } + + return () => { + canvasElement.removeEventListener("pointerdown", onPointerDown); + canvasElement.removeEventListener("pointermove", onPointerMove); + canvasElement.removeEventListener("contextmenu", onContextMenu); + canvasElement.removeEventListener("pointerup", onPointerUp); + canvasElement.removeEventListener("keydown", onKeyDown); + helper.enabled = false; + helper.dispose(); + }; + }, [ + camera, + controls, + scene, + toggleView, + selectedAssets, + copiedObjects, + pastedObjects, + duplicatedObjects, + movedObjects, + socket, + floorItems, + rotatedObjects, + activeModule, + ]); + + useEffect(() => { + if (activeModule !== "builder") { + clearSelection(); + } + }, [activeModule]); + + useFrame(() => { + if ( + pastedObjects.length === 0 && + duplicatedObjects.length === 0 && + movedObjects.length === 0 && + rotatedObjects.length === 0 + ) { + selectionGroup.current.position.set(0, 0, 0); + } + }); + + const selectAssets = () => { + selectionBox.endPoint.set(pointer.x, pointer.y, 0); + if (controls) (controls as any).enabled = true; + + let selectedObjects = selectionBox.select(); + let Objects = new Set(); + + selectedObjects.map((object) => { + let currentObject: THREE.Object3D | null = object; + while (currentObject) { + if (currentObject.userData.modelId) { + Objects.add(currentObject); + break; } + currentObject = currentObject.parent || null; + } }); - const selectAssets = () => { - selectionBox.endPoint.set(pointer.x, pointer.y, 0); - if (controls) (controls as any).enabled = true; + if (Objects.size === 0) { + clearSelection(); + return; + } - let selectedObjects = selectionBox.select(); - let Objects = new Set(); + const updatedSelections = new Set(selectedAssets); + Objects.forEach((obj) => { + updatedSelections.has(obj) + ? updatedSelections.delete(obj) + : updatedSelections.add(obj); + }); - selectedObjects.map((object) => { - let currentObject: THREE.Object3D | null = object; - while (currentObject) { - if (currentObject.userData.modelId) { - Objects.add(currentObject); - break; - } - currentObject = currentObject.parent || null; - } + const selected = Array.from(updatedSelections); + + setSelectedAssets(selected); + }; + + const clearSelection = () => { + selectionGroup.current.children = []; + selectionGroup.current.position.set(0, 0, 0); + selectionGroup.current.rotation.set(0, 0, 0); + setpastedObjects([]); + setDuplicatedObjects([]); + setSelectedAssets([]); + }; + const updateBackend = async ( + updatedPaths: ( + | SimulationTypes.ConveyorEventsSchema + | SimulationTypes.VehicleEventsSchema + | SimulationTypes.StaticMachineEventsSchema + | SimulationTypes.ArmBotEventsSchema + )[] + ) => { + if (updatedPaths.length === 0) return; + const email = localStorage.getItem("email"); + const organization = email ? email.split("@")[1].split(".")[0] : ""; + + updatedPaths.forEach(async (updatedPath) => { + if (updatedPath.type === "Conveyor") { + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed } + // ); + + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { + type: "Conveyor", + points: updatedPath.points, + speed: updatedPath.speed, + }, + }; + + socket.emit("v2:model-asset:updateEventData", data); + } else if (updatedPath.type === "Vehicle") { + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "Vehicle", points: updatedPath.points } + // ); + + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { type: "Vehicle", points: updatedPath.points }, + }; + + socket.emit("v2:model-asset:updateEventData", data); + } else if (updatedPath.type === "StaticMachine") { + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "StaticMachine", points: updatedPath.points } + // ); + + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { type: "StaticMachine", points: updatedPath.points }, + }; + + socket.emit("v2:model-asset:updateEventData", data); + } else if (updatedPath.type === "ArmBot") { + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "ArmBot", points: updatedPath.points } + // ); + + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { type: "ArmBot", points: updatedPath.points }, + }; + + socket.emit("v2:model-asset:updateEventData", data); + } + }); + }; + + // const removeConnection = (modelUUID: any) => { + // + // const removedPath = simulationStates?.flatMap((state) => { + // let shouldInclude = false; + + // if (state.type === "Conveyor") { + // state.points.forEach((point: any) => { + // const sourceMatch = + // point.connections?.source?.modelUUID === modelUUID; + // const targetMatch = point.connections?.targets?.some( + // (target: any) => target.modelUUID === modelUUID + // ); + + // if (sourceMatch || targetMatch) shouldInclude = true; + // }); + // } + + // if (state.type === "Vehicle") { + // const targetMatch = state.points.connections?.targets?.some( + // (target: any) => target.modelUUID === modelUUID + // ); + + // if (targetMatch) shouldInclude = true; + // } + + // if (state.type === "StaticMachine") { + // const targetMatch = state.points.connections?.targets?.some( + // (target: any) => target.modelUUID === modelUUID + // ); + + // if (targetMatch) shouldInclude = true; + // } + + // if (state.type === "ArmBot") { + // const sourceMatch = + // state.points.connections?.source?.modelUUID === modelUUID; + // const targetMatch = state.points.connections?.targets?.some( + // (target: any) => target.modelUUID === modelUUID + // ); + + // const processMatch = + // state.points.actions?.processes?.some( + // (process: any) => + // process.startPoint === modelUUID || process.endPoint === modelUUID + // ) ?? false; + + // if (sourceMatch || targetMatch || processMatch) shouldInclude = true; + // } + + // return shouldInclude ? [state] : []; + // }); + // updateBackend(removedPath); + // + // return removedPath; + // // updateBackend(updatedPaths); + + // // setSimulationStates(updatedStates); + // }; + // const removeConnection = (modelUUIDs: any[]) => { + // + // const removedPath = simulationStates?.flatMap((state) => { + // let shouldInclude = false; + + // if (state.type === "Conveyor") { + // state.points.forEach((point: any) => { + // const sourceMatch = modelUUIDs.includes( + // point.connections?.source?.modelUUID + // ); + // const targetMatch = point.connections?.targets?.some((target: any) => + // modelUUIDs.includes(target.modelUUID) + // ); + + // if (sourceMatch || targetMatch) shouldInclude = true; + // }); + // } + + // if (state.type === "Vehicle") { + // const targetMatch = state.points.connections?.targets?.some( + // (target: any) => modelUUIDs.includes(target.modelUUID) + // ); + + // if (targetMatch) shouldInclude = true; + // } + + // if (state.type === "StaticMachine") { + // const targetMatch = state.points.connections?.targets?.some( + // (target: any) => modelUUIDs.includes(target.modelUUID) + // ); + + // if (targetMatch) shouldInclude = true; + // } + + // if (state.type === "ArmBot") { + // const sourceMatch = modelUUIDs.includes( + // state.points.connections?.source?.modelUUID + // ); + // const targetMatch = state.points.connections?.targets?.some( + // (target: any) => modelUUIDs.includes(target.modelUUID) + // ); + + // const processMatch = + // state.points.actions?.processes?.some( + // (process: any) => + // modelUUIDs.includes(process.startPoint) || + // modelUUIDs.includes(process.endPoint) + // ) ?? false; + + // if (sourceMatch || targetMatch || processMatch) shouldInclude = true; + // } + + // return shouldInclude ? [state] : []; + // }); + // updateBackend(removedPath); + // + // return removedPath; + // }; + + const removeConnection = (modelUUIDs: any[]) => { + const removedPath = simulationStates?.flatMap((state: any) => { + let shouldInclude = false; + + // Conveyor type + if (state.type === "Conveyor") { + state.points.forEach((point: any) => { + const sourceMatch = modelUUIDs.includes( + point.connections?.source?.modelUUID + ); + const targetMatch = point.connections?.targets?.some((target: any) => + modelUUIDs.includes(target.modelUUID) + ); + + if (sourceMatch) { + point.connections.source = {}; + shouldInclude = true; + } + + if (targetMatch) { + point.connections.targets = []; + shouldInclude = true; + } }); + } + // Vehicle & StaticMachine types + if (state.type === "Vehicle") { + const targets = state.points?.connections?.targets || []; + const targetMatch = targets.some((target: any) => + modelUUIDs.includes(target.modelUUID) + ); - if (Objects.size === 0) { - clearSelection(); - return; + if (targetMatch) { + state.points.connections.targets = []; + shouldInclude = true; + } + } + if (state.type === "StaticMachine") { + const targets = state.points?.connections?.targets || []; + const targetMatch = targets.some((target: any) => + modelUUIDs.includes(target.modelUUID) + ); + + if (targetMatch) { + state.points.connections.targets = []; + shouldInclude = true; + } + } + + // ArmBot type + if (state.type === "ArmBot") { + const sourceMatch = modelUUIDs.includes( + state.points.connections?.source?.modelUUID + ); + console.log("model", modelUUIDs); + console.log("state.points.connections: ", state.points); + + const targetMatch = state.points.connections?.targets?.some( + (target: any) => modelUUIDs.includes(target.modelUUID) + ); + // state.points.actions.processes = state.points.actions.processes.filter( + // (process: any) => + // console.log( + // !modelUUIDs.includes(process.startPoint), + // !modelUUIDs.includes(process.endPoint), + // modelUUIDs, + // process.startPoint, + // process.endPoint + // ) + // ); + + // shouldInclude = true; + + // const processMatches = state.points.actions?.processes?.some( + // (process: any) => + // console.log( + // "process: ", + // process, + // modelUUIDs, + // process.startPoint, + // process.endPoint + // ) + // // modelUUIDs.includes(process.startPoint) || + // // modelUUIDs.includes(process.endPoint) + // ); + // const processMatch = state.points.actions?.processes?.some( + // (process: any) => + // modelUUIDs.includes(String(process.startPoint)) || + // modelUUIDs.includes(String(process.endPoint)) + // ); + + // console.log("processMatch: ", processMatch); + if (sourceMatch) { + state.points.connections.source = {}; + shouldInclude = true; } - const updatedSelections = new Set(selectedAssets); - Objects.forEach((obj) => { updatedSelections.has(obj) ? updatedSelections.delete(obj) : updatedSelections.add(obj); }); - - const selected = Array.from(updatedSelections); - - setSelectedAssets(selected); - }; - - const clearSelection = () => { - selectionGroup.current.children = []; - selectionGroup.current.position.set(0, 0, 0); - selectionGroup.current.rotation.set(0, 0, 0); - setpastedObjects([]); - setDuplicatedObjects([]); - setSelectedAssets([]); - }; - const updateBackend = async (updatedPaths: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { - if (updatedPaths.length === 0) return; - const email = localStorage.getItem("email"); - const organization = email ? email.split("@")[1].split(".")[0] : ""; - - updatedPaths.forEach(async (updatedPath) => { - - if (updatedPath.type === "Conveyor") { - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { - type: "Conveyor", - points: updatedPath.points, - speed: updatedPath.speed, - }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - - } else if (updatedPath.type === "Vehicle") { - - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "Vehicle", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "Vehicle", points: updatedPath.points }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - - } else if (updatedPath.type === "StaticMachine") { - - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "StaticMachine", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "StaticMachine", points: updatedPath.points }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - - } else if (updatedPath.type === "ArmBot") { - - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "ArmBot", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "ArmBot", points: updatedPath.points }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - } - - }); - }; - - // const removeConnection = (modelUUID: any) => { - // - // const removedPath = simulationStates?.flatMap((state) => { - // let shouldInclude = false; - - // if (state.type === "Conveyor") { - // state.points.forEach((point: any) => { - // const sourceMatch = - // point.connections?.source?.modelUUID === modelUUID; - // const targetMatch = point.connections?.targets?.some( - // (target: any) => target.modelUUID === modelUUID - // ); - - // if (sourceMatch || targetMatch) shouldInclude = true; - // }); - // } - - // if (state.type === "Vehicle") { - // const targetMatch = state.points.connections?.targets?.some( - // (target: any) => target.modelUUID === modelUUID - // ); - - // if (targetMatch) shouldInclude = true; - // } - - // if (state.type === "StaticMachine") { - // const targetMatch = state.points.connections?.targets?.some( - // (target: any) => target.modelUUID === modelUUID - // ); - - // if (targetMatch) shouldInclude = true; - // } - - // if (state.type === "ArmBot") { - // const sourceMatch = - // state.points.connections?.source?.modelUUID === modelUUID; - // const targetMatch = state.points.connections?.targets?.some( - // (target: any) => target.modelUUID === modelUUID - // ); - - // const processMatch = - // state.points.actions?.processes?.some( - // (process: any) => - // process.startPoint === modelUUID || process.endPoint === modelUUID - // ) ?? false; - - // if (sourceMatch || targetMatch || processMatch) shouldInclude = true; - // } - - // return shouldInclude ? [state] : []; - // }); - // updateBackend(removedPath); - // - // return removedPath; - // // updateBackend(updatedPaths); - - // // setSimulationStates(updatedStates); - // }; - // const removeConnection = (modelUUIDs: any[]) => { - // - // const removedPath = simulationStates?.flatMap((state) => { - // let shouldInclude = false; - - // if (state.type === "Conveyor") { - // state.points.forEach((point: any) => { - // const sourceMatch = modelUUIDs.includes( - // point.connections?.source?.modelUUID - // ); - // const targetMatch = point.connections?.targets?.some((target: any) => - // modelUUIDs.includes(target.modelUUID) - // ); - - // if (sourceMatch || targetMatch) shouldInclude = true; - // }); - // } - - // if (state.type === "Vehicle") { - // const targetMatch = state.points.connections?.targets?.some( - // (target: any) => modelUUIDs.includes(target.modelUUID) - // ); - - // if (targetMatch) shouldInclude = true; - // } - - // if (state.type === "StaticMachine") { - // const targetMatch = state.points.connections?.targets?.some( - // (target: any) => modelUUIDs.includes(target.modelUUID) - // ); - - // if (targetMatch) shouldInclude = true; - // } - - // if (state.type === "ArmBot") { - // const sourceMatch = modelUUIDs.includes( - // state.points.connections?.source?.modelUUID - // ); - // const targetMatch = state.points.connections?.targets?.some( - // (target: any) => modelUUIDs.includes(target.modelUUID) - // ); - - // const processMatch = - // state.points.actions?.processes?.some( - // (process: any) => - // modelUUIDs.includes(process.startPoint) || - // modelUUIDs.includes(process.endPoint) - // ) ?? false; - - // if (sourceMatch || targetMatch || processMatch) shouldInclude = true; - // } - - // return shouldInclude ? [state] : []; - // }); - // updateBackend(removedPath); - // - // return removedPath; - // }; - - const removeConnection = (modelUUIDs: any[]) => { - const removedPath = simulationStates?.flatMap((state: any) => { - let shouldInclude = false; - - // Conveyor type - if (state.type === "Conveyor") { - state.points.forEach((point: any) => { - const sourceMatch = modelUUIDs.includes(point.connections?.source?.modelUUID); - const targetMatch = point.connections?.targets?.some((target: any) => modelUUIDs.includes(target.modelUUID)); - - if (sourceMatch) { - point.connections.source = {}; - shouldInclude = true; - } - - if (targetMatch) { - point.connections.targets = []; - shouldInclude = true; - } - }); - } - - // Vehicle & StaticMachine types - if (state.type === "Vehicle") { - const targets = state.points?.connections?.targets || []; - const targetMatch = targets.some((target: any) => modelUUIDs.includes(target.modelUUID)); - - if (targetMatch) { - state.points.connections.targets = []; - shouldInclude = true; - } - } - if (state.type === "StaticMachine") { - const targets = state.points?.connections?.targets || []; - const targetMatch = targets.some((target: any) => modelUUIDs.includes(target.modelUUID)); - - if (targetMatch) { - state.points.connections.targets = []; - shouldInclude = true; - } - } - - // ArmBot type - if (state.type === "ArmBot") { - const sourceMatch = modelUUIDs.includes(state.points.connections?.source?.modelUUID); - - const targetMatch = state.points.connections?.targets?.some( - (target: any) => modelUUIDs.includes(target.modelUUID) - ); - // state.points.actions.processes = state.points.actions.processes.filter( - // (process: any) => - // console.log( - // !modelUUIDs.includes(process.startPoint), - // !modelUUIDs.includes(process.endPoint), - // modelUUIDs, - // process.startPoint, - // process.endPoint - // ) - // ); - - // shouldInclude = true; - - // const processMatches = state.points.actions?.processes?.some( - // (process: any) => - // console.log( - // "process: ", - // process, - // modelUUIDs, - // process.startPoint, - // process.endPoint - // ) - // // modelUUIDs.includes(process.startPoint) || - // // modelUUIDs.includes(process.endPoint) - // ); - // const processMatch = state.points.actions?.processes?.some( - // (process: any) => - // modelUUIDs.includes(String(process.startPoint)) || - // modelUUIDs.includes(String(process.endPoint)) - // ); - - // console.log("processMatch: ", processMatch); - if (sourceMatch) { - state.points.connections.source = {}; - shouldInclude = true; - } - - if (targetMatch) { - state.points.connections.targets = - state.points.connections.targets.filter((target: any) => !modelUUIDs.includes(target.modelUUID)); - shouldInclude = true; - } - - // console.log("processMatch: ", processMatch); - // if (processMatch) { - // state.points.actions.processes = - // state.points.actions.processes.filter((process: any) => { - // const shouldRemove = - // modelUUIDs.includes(process.startPoint) || - // modelUUIDs.includes(process.endPoint); - - // console.log("shouldRemove: ", shouldRemove); - // return !shouldRemove; - // }); - // shouldInclude = true; - // } - } - - return shouldInclude ? [state] : []; - }); - - updateBackend(removedPath); - return removedPath; - }; - - const deleteSelection = () => { - if (selectedAssets.length > 0 && duplicatedObjects.length === 0) { - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; - - const storedItems = JSON.parse(localStorage.getItem("FloorItems") || "[]"); - const selectedUUIDs = selectedAssets.map((mesh: THREE.Object3D) => mesh.uuid); - - const updatedStoredItems = storedItems.filter((item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid)); - localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); - - selectedAssets.forEach((selectedMesh: THREE.Object3D) => { - //REST - - // const response = await deleteFloorItem(organization, selectedMesh.uuid, selectedMesh.userData.name); - - //SOCKET - - const data = { - organization: organization, - modeluuid: selectedMesh.uuid, - modelname: selectedMesh.userData.name, - socketId: socket.id, - }; - - socket.emit("v2:model-asset:delete", data); - - selectedMesh.traverse((child: THREE.Object3D) => { - if (child instanceof THREE.Mesh) { - if (child.geometry) child.geometry.dispose(); - if (Array.isArray(child.material)) { - child.material.forEach((material) => { - if (material.map) material.map.dispose(); - material.dispose(); - }); - } else if (child.material) { - if (child.material.map) child.material.map.dispose(); - child.material.dispose(); - } - } - }); - - setSimulationStates((prevEvents: (| SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { - const updatedEvents = (prevEvents || []).filter( - (event) => event.modeluuid !== selectedMesh.uuid - ); - return updatedEvents; - } - ); - - itemsGroupRef.current?.remove(selectedMesh); - }); - - const allUUIDs = selectedAssets.map((val: any) => val.uuid); - removeConnection(allUUIDs); - - // const removedPath = simulationStates?.flatMap((path: any) => { - // let shouldInclude = false; - - // if (Array.isArray(path.points)) { - // path.points.forEach((point: any) => { - // const sourceMatch = - // point.connections?.source?.modelUUID === selectedAssets[0].uuid; - // const targetMatch = point.connections?.targets?.some( - // (target: any) => target.modelUUID === selectedAssets[0].uuid - // ); - - // if (sourceMatch) { - // point.connections.source = {}; - // shouldInclude = true; - // } - - // if (targetMatch) { - // point.connections.targets = []; - // shouldInclude = true; - // } - // }); - // } else { - // const sourceMatch = - // path.connections?.source?.modelUUID === selectedAssets[0].uuid; - // const targetMatch = path.connections?.targets?.some( - // (target: any) => target.modelUUID === selectedAssets[0].uuid - // ); - - // if (sourceMatch) { - // path.connections.source = {}; - // shouldInclude = true; - // } - - // if (targetMatch) { - // path.connections.targets = []; - // shouldInclude = true; - // } - // } - - // return shouldInclude ? [path] : []; - // }); - // updateBackend(removedPath); - - const updatedItems = floorItems.filter( - (item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid) + if (targetMatch) { + state.points.connections.targets = + state.points.connections.targets.filter( + (target: any) => !modelUUIDs.includes(target.modelUUID) ); - setFloorItems(updatedItems); + shouldInclude = true; } - toast.success("Selected models removed!"); - clearSelection(); - }; - return ( - <> - - - - - + // console.log("processMatch: ", processMatch); + // if (processMatch) { + // state.points.actions.processes = + // state.points.actions.processes.filter((process: any) => { + // const shouldRemove = + // modelUUIDs.includes(process.startPoint) || + // modelUUIDs.includes(process.endPoint); + // console.log("shouldRemove: ", shouldRemove); + // return !shouldRemove; + // }); + // shouldInclude = true; + // } + } - + return shouldInclude ? [state] : []; + }); - + updateBackend(removedPath); + return removedPath; + }; - + const deleteSelection = () => { + if (selectedAssets.length > 0 && duplicatedObjects.length === 0) { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; - - - ); + const storedItems = JSON.parse( + localStorage.getItem("FloorItems") || "[]" + ); + const selectedUUIDs = selectedAssets.map( + (mesh: THREE.Object3D) => mesh.uuid + ); + + const updatedStoredItems = storedItems.filter( + (item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid) + ); + localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); + + selectedAssets.forEach((selectedMesh: THREE.Object3D) => { + //REST + + // const response = await deleteFloorItem(organization, selectedMesh.uuid, selectedMesh.userData.name); + + //SOCKET + + const data = { + organization: organization, + modeluuid: selectedMesh.uuid, + modelname: selectedMesh.userData.name, + socketId: socket.id, + }; + + socket.emit("v2:model-asset:delete", data); + + selectedMesh.traverse((child: THREE.Object3D) => { + if (child instanceof THREE.Mesh) { + if (child.geometry) child.geometry.dispose(); + if (Array.isArray(child.material)) { + child.material.forEach((material) => { + if (material.map) material.map.dispose(); + material.dispose(); + }); + } else if (child.material) { + if (child.material.map) child.material.map.dispose(); + child.material.dispose(); + } + } + }); + + setSimulationStates( + ( + prevEvents: ( + | SimulationTypes.ConveyorEventsSchema + | SimulationTypes.VehicleEventsSchema + | SimulationTypes.StaticMachineEventsSchema + | SimulationTypes.ArmBotEventsSchema + )[] + ) => { + const updatedEvents = (prevEvents || []).filter( + (event) => event.modeluuid !== selectedMesh.uuid + ); + return updatedEvents; + } + ); + + itemsGroupRef.current?.remove(selectedMesh); + }); + + const allUUIDs = selectedAssets.map((val: any) => val.uuid); + removeConnection(allUUIDs); + + // const removedPath = simulationStates?.flatMap((path: any) => { + // let shouldInclude = false; + + // if (Array.isArray(path.points)) { + // path.points.forEach((point: any) => { + // const sourceMatch = + // point.connections?.source?.modelUUID === selectedAssets[0].uuid; + // const targetMatch = point.connections?.targets?.some( + // (target: any) => target.modelUUID === selectedAssets[0].uuid + // ); + + // if (sourceMatch) { + // point.connections.source = {}; + // shouldInclude = true; + // } + + // if (targetMatch) { + // point.connections.targets = []; + // shouldInclude = true; + // } + // }); + // } else { + // const sourceMatch = + // path.connections?.source?.modelUUID === selectedAssets[0].uuid; + // const targetMatch = path.connections?.targets?.some( + // (target: any) => target.modelUUID === selectedAssets[0].uuid + // ); + + // if (sourceMatch) { + // path.connections.source = {}; + // shouldInclude = true; + // } + + // if (targetMatch) { + // path.connections.targets = []; + // shouldInclude = true; + // } + // } + + // return shouldInclude ? [path] : []; + // }); + // updateBackend(removedPath); + + const updatedItems = floorItems.filter( + (item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid) + ); + setFloorItems(updatedItems); + } + toast.success("Selected models removed!"); + clearSelection(); + }; + + return ( + <> + + + + + + + + + + + + + + + ); }; export default SelectionControls; From c2a29fc8930da44087e4a2532a8537b6f90c72e1 Mon Sep 17 00:00:00 2001 From: Poovizhi99 Date: Tue, 15 Apr 2025 15:37:11 +0530 Subject: [PATCH 3/5] integrated path while deleting the asset --- .../controls/selection/selectionControls.tsx | 391 +++++------------- 1 file changed, 113 insertions(+), 278 deletions(-) diff --git a/app/src/modules/scene/controls/selection/selectionControls.tsx b/app/src/modules/scene/controls/selection/selectionControls.tsx index 80a7518..8d7c4cc 100644 --- a/app/src/modules/scene/controls/selection/selectionControls.tsx +++ b/app/src/modules/scene/controls/selection/selectionControls.tsx @@ -323,247 +323,124 @@ const SelectionControls: React.FC = () => { }); }; - // const removeConnection = (modelUUID: any) => { - // - // const removedPath = simulationStates?.flatMap((state) => { - // let shouldInclude = false; - - // if (state.type === "Conveyor") { - // state.points.forEach((point: any) => { - // const sourceMatch = - // point.connections?.source?.modelUUID === modelUUID; - // const targetMatch = point.connections?.targets?.some( - // (target: any) => target.modelUUID === modelUUID - // ); - - // if (sourceMatch || targetMatch) shouldInclude = true; - // }); - // } - - // if (state.type === "Vehicle") { - // const targetMatch = state.points.connections?.targets?.some( - // (target: any) => target.modelUUID === modelUUID - // ); - - // if (targetMatch) shouldInclude = true; - // } - - // if (state.type === "StaticMachine") { - // const targetMatch = state.points.connections?.targets?.some( - // (target: any) => target.modelUUID === modelUUID - // ); - - // if (targetMatch) shouldInclude = true; - // } - - // if (state.type === "ArmBot") { - // const sourceMatch = - // state.points.connections?.source?.modelUUID === modelUUID; - // const targetMatch = state.points.connections?.targets?.some( - // (target: any) => target.modelUUID === modelUUID - // ); - - // const processMatch = - // state.points.actions?.processes?.some( - // (process: any) => - // process.startPoint === modelUUID || process.endPoint === modelUUID - // ) ?? false; - - // if (sourceMatch || targetMatch || processMatch) shouldInclude = true; - // } - - // return shouldInclude ? [state] : []; - // }); - // updateBackend(removedPath); - // - // return removedPath; - // // updateBackend(updatedPaths); - - // // setSimulationStates(updatedStates); - // }; - // const removeConnection = (modelUUIDs: any[]) => { - // - // const removedPath = simulationStates?.flatMap((state) => { - // let shouldInclude = false; - - // if (state.type === "Conveyor") { - // state.points.forEach((point: any) => { - // const sourceMatch = modelUUIDs.includes( - // point.connections?.source?.modelUUID - // ); - // const targetMatch = point.connections?.targets?.some((target: any) => - // modelUUIDs.includes(target.modelUUID) - // ); - - // if (sourceMatch || targetMatch) shouldInclude = true; - // }); - // } - - // if (state.type === "Vehicle") { - // const targetMatch = state.points.connections?.targets?.some( - // (target: any) => modelUUIDs.includes(target.modelUUID) - // ); - - // if (targetMatch) shouldInclude = true; - // } - - // if (state.type === "StaticMachine") { - // const targetMatch = state.points.connections?.targets?.some( - // (target: any) => modelUUIDs.includes(target.modelUUID) - // ); - - // if (targetMatch) shouldInclude = true; - // } - - // if (state.type === "ArmBot") { - // const sourceMatch = modelUUIDs.includes( - // state.points.connections?.source?.modelUUID - // ); - // const targetMatch = state.points.connections?.targets?.some( - // (target: any) => modelUUIDs.includes(target.modelUUID) - // ); - - // const processMatch = - // state.points.actions?.processes?.some( - // (process: any) => - // modelUUIDs.includes(process.startPoint) || - // modelUUIDs.includes(process.endPoint) - // ) ?? false; - - // if (sourceMatch || targetMatch || processMatch) shouldInclude = true; - // } - - // return shouldInclude ? [state] : []; - // }); - // updateBackend(removedPath); - // - // return removedPath; - // }; - - const removeConnection = (modelUUIDs: any[]) => { - const removedPath = simulationStates?.flatMap((state: any) => { - let shouldInclude = false; - - // Conveyor type + const removeConnections = (deletedModelUUIDs: string[]) => { + const updatedStates = simulationStates.map((state) => { + // Handle Conveyor if (state.type === "Conveyor") { - state.points.forEach((point: any) => { - const sourceMatch = modelUUIDs.includes( - point.connections?.source?.modelUUID - ); - const targetMatch = point.connections?.targets?.some((target: any) => - modelUUIDs.includes(target.modelUUID) - ); - - if (sourceMatch) { - point.connections.source = {}; - shouldInclude = true; - } - - if (targetMatch) { - point.connections.targets = []; - shouldInclude = true; - } - }); - } - // Vehicle & StaticMachine types - if (state.type === "Vehicle") { - const targets = state.points?.connections?.targets || []; - const targetMatch = targets.some((target: any) => - modelUUIDs.includes(target.modelUUID) - ); - - if (targetMatch) { - state.points.connections.targets = []; - shouldInclude = true; - } - } - if (state.type === "StaticMachine") { - const targets = state.points?.connections?.targets || []; - const targetMatch = targets.some((target: any) => - modelUUIDs.includes(target.modelUUID) - ); - - if (targetMatch) { - state.points.connections.targets = []; - shouldInclude = true; - } + const updatedConveyor: SimulationTypes.ConveyorEventsSchema = { + ...state, + points: state.points.map((point) => { + return { + ...point, + connections: { + ...point.connections, + targets: point.connections.targets.filter( + (target) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + }; + }), + }; + return updatedConveyor; } - // ArmBot type - if (state.type === "ArmBot") { - const sourceMatch = modelUUIDs.includes( - state.points.connections?.source?.modelUUID - ); - console.log("model", modelUUIDs); - console.log("state.points.connections: ", state.points); - - const targetMatch = state.points.connections?.targets?.some( - (target: any) => modelUUIDs.includes(target.modelUUID) - ); - // state.points.actions.processes = state.points.actions.processes.filter( - // (process: any) => - // console.log( - // !modelUUIDs.includes(process.startPoint), - // !modelUUIDs.includes(process.endPoint), - // modelUUIDs, - // process.startPoint, - // process.endPoint - // ) - // ); - - // shouldInclude = true; - - // const processMatches = state.points.actions?.processes?.some( - // (process: any) => - // console.log( - // "process: ", - // process, - // modelUUIDs, - // process.startPoint, - // process.endPoint - // ) - // // modelUUIDs.includes(process.startPoint) || - // // modelUUIDs.includes(process.endPoint) - // ); - // const processMatch = state.points.actions?.processes?.some( - // (process: any) => - // modelUUIDs.includes(String(process.startPoint)) || - // modelUUIDs.includes(String(process.endPoint)) - // ); - - // console.log("processMatch: ", processMatch); - if (sourceMatch) { - state.points.connections.source = {}; - shouldInclude = true; - } - - if (targetMatch) { - state.points.connections.targets = - state.points.connections.targets.filter( - (target: any) => !modelUUIDs.includes(target.modelUUID) - ); - shouldInclude = true; - } - - // console.log("processMatch: ", processMatch); - // if (processMatch) { - // state.points.actions.processes = - // state.points.actions.processes.filter((process: any) => { - // const shouldRemove = - // modelUUIDs.includes(process.startPoint) || - // modelUUIDs.includes(process.endPoint); - // console.log("shouldRemove: ", shouldRemove); - // return !shouldRemove; - // }); - // shouldInclude = true; - // } + // Handle Vehicle + else if (state.type === "Vehicle") { + const updatedVehicle: SimulationTypes.VehicleEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter( + (target) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + }, + }; + return updatedVehicle; } - return shouldInclude ? [state] : []; + // Handle StaticMachine + else if (state.type === "StaticMachine") { + const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = + { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter( + (target) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + }, + }; + return updatedStaticMachine; + } + + // Handle ArmBot + else if (state.type === "ArmBot") { + const updatedArmBot: SimulationTypes.ArmBotEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + + targets: state.points.connections.targets.filter( + (target: any) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + actions: { + ...state.points.actions, + processes: (state.points.actions.processes = + state.points.actions.processes?.filter((process) => { + const matchedStates = simulationStates.filter((s) => + deletedModelUUIDs.includes(s.modeluuid) + ); + + if (matchedStates.length > 0) { + if (matchedStates[0]?.type === "StaticMachine") { + const trigPoints = matchedStates[0]?.points; + + return !( + process.triggerId === trigPoints?.triggers?.uuid + ); + } else if (matchedStates[0]?.type === "Conveyor") { + const trigPoints = matchedStates[0]?.points; + + if (Array.isArray(trigPoints)) { + const nonEmptyTriggers = trigPoints.filter( + (point) => + point && point.triggers && point.triggers.length > 0 + ); + + const allTriggerUUIDs = nonEmptyTriggers + .flatMap((point) => point.triggers) + .map((trigger) => trigger.uuid); + + return !allTriggerUUIDs.includes(process.triggerId); + } + } + } + return true; + })), + }, + }, + }; + return updatedArmBot; + } + + return state; }); - updateBackend(removedPath); - return removedPath; + const filteredStates = updatedStates.filter( + (state) => !deletedModelUUIDs.includes(state.modeluuid) + ); + + updateBackend(filteredStates); + setSimulationStates(filteredStates); }; const deleteSelection = () => { @@ -633,51 +510,9 @@ const SelectionControls: React.FC = () => { itemsGroupRef.current?.remove(selectedMesh); }); + console.log("selectedAssets: ", selectedAssets); const allUUIDs = selectedAssets.map((val: any) => val.uuid); - removeConnection(allUUIDs); - - // const removedPath = simulationStates?.flatMap((path: any) => { - // let shouldInclude = false; - - // if (Array.isArray(path.points)) { - // path.points.forEach((point: any) => { - // const sourceMatch = - // point.connections?.source?.modelUUID === selectedAssets[0].uuid; - // const targetMatch = point.connections?.targets?.some( - // (target: any) => target.modelUUID === selectedAssets[0].uuid - // ); - - // if (sourceMatch) { - // point.connections.source = {}; - // shouldInclude = true; - // } - - // if (targetMatch) { - // point.connections.targets = []; - // shouldInclude = true; - // } - // }); - // } else { - // const sourceMatch = - // path.connections?.source?.modelUUID === selectedAssets[0].uuid; - // const targetMatch = path.connections?.targets?.some( - // (target: any) => target.modelUUID === selectedAssets[0].uuid - // ); - - // if (sourceMatch) { - // path.connections.source = {}; - // shouldInclude = true; - // } - - // if (targetMatch) { - // path.connections.targets = []; - // shouldInclude = true; - // } - // } - - // return shouldInclude ? [path] : []; - // }); - // updateBackend(removedPath); + removeConnections(allUUIDs); const updatedItems = floorItems.filter( (item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid) From f62d231a79e6709fedd6ee94034ac13c53091866 Mon Sep 17 00:00:00 2001 From: Poovizhi99 Date: Tue, 15 Apr 2025 15:39:05 +0530 Subject: [PATCH 4/5] integrated the removeConnections in path connector --- .../modules/simulation/path/pathConnector.tsx | 158 +++++++++++++++--- 1 file changed, 139 insertions(+), 19 deletions(-) diff --git a/app/src/modules/simulation/path/pathConnector.tsx b/app/src/modules/simulation/path/pathConnector.tsx index 1e93bd5..fe3bb1b 100644 --- a/app/src/modules/simulation/path/pathConnector.tsx +++ b/app/src/modules/simulation/path/pathConnector.tsx @@ -1133,26 +1133,27 @@ function PathConnector({ (state.modeluuid === connection2.model && state.points.uuid === connection2.point) ) { - const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter((target) => { - return !( - (target.modelUUID === connection1.model && - target.pointUUID === connection1.point) || - (target.modelUUID === connection2.model && - target.pointUUID === connection2.point) - ); - }), + const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = + { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter((target) => { + return !( + (target.modelUUID === connection1.model && + target.pointUUID === connection1.point) || + (target.modelUUID === connection2.model && + target.pointUUID === connection2.point) + ); + }), + }, + // Ensure all required StaticMachine point properties are included + actions: state.points.actions, + triggers: state.points.triggers, }, - // Ensure all required StaticMachine point properties are included - actions: state.points.actions, - triggers: state.points.triggers, - }, - }; + }; return updatedStaticMachine; } } @@ -1211,6 +1212,125 @@ function PathConnector({ setSimulationStates(updatedStates); }; + const removeConnection = (deletedModelUUIDs: string[]) => { + const updatedStates = simulationStates.map((state) => { + // Handle Conveyor + if (state.type === "Conveyor") { + const updatedConveyor: SimulationTypes.ConveyorEventsSchema = { + ...state, + points: state.points.map((point) => { + return { + ...point, + connections: { + ...point.connections, + targets: point.connections.targets.filter( + (target) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + }; + }), + }; + return updatedConveyor; + } + + // Handle Vehicle + else if (state.type === "Vehicle") { + const updatedVehicle: SimulationTypes.VehicleEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter( + (target) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + }, + }; + return updatedVehicle; + } + + // Handle StaticMachine + else if (state.type === "StaticMachine") { + const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = + { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter( + (target) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + }, + }; + return updatedStaticMachine; + } + + // Handle ArmBot + else if (state.type === "ArmBot") { + const updatedArmBot: SimulationTypes.ArmBotEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + + targets: state.points.connections.targets.filter( + (target: any) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + actions: { + ...state.points.actions, + processes: (state.points.actions.processes = + state.points.actions.processes?.filter((process) => { + const matchedStates = simulationStates.filter((s) => + deletedModelUUIDs.includes(s.modeluuid) + ); + + if (matchedStates.length > 0) { + if (matchedStates[0]?.type === "StaticMachine") { + const trigPoints = matchedStates[0]?.points; + + return !( + process.triggerId === trigPoints?.triggers?.uuid + ); + } else if (matchedStates[0]?.type === "Conveyor") { + const trigPoints = matchedStates[0]?.points; + + if (Array.isArray(trigPoints)) { + const nonEmptyTriggers = trigPoints.filter( + (point) => + point && point.triggers && point.triggers.length > 0 + ); + + const allTriggerUUIDs = nonEmptyTriggers + .flatMap((point) => point.triggers) + .map((trigger) => trigger.uuid); + + return !allTriggerUUIDs.includes(process.triggerId); + } + } + } + return true; + })), + }, + }, + }; + return updatedArmBot; + } + + return state; + }); + + const filteredStates = updatedStates.filter( + (state) => !deletedModelUUIDs.includes(state.modeluuid) + ); + + updateBackend(filteredStates); + setSimulationStates(filteredStates); + }; return ( From a26e0dacd08d743bcfd55a9e2169e45bb0eed921 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Wed, 16 Apr 2025 10:03:01 +0530 Subject: [PATCH 5/5] Implement code changes to enhance functionality and improve performance --- .../controls/selection/selectionControls.tsx | 978 +++--- .../modules/simulation/path/pathConnector.tsx | 2784 ++++++++--------- 2 files changed, 1646 insertions(+), 2116 deletions(-) diff --git a/app/src/modules/scene/controls/selection/selectionControls.tsx b/app/src/modules/scene/controls/selection/selectionControls.tsx index aa2bca4..ece6924 100644 --- a/app/src/modules/scene/controls/selection/selectionControls.tsx +++ b/app/src/modules/scene/controls/selection/selectionControls.tsx @@ -3,13 +3,7 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { SelectionBox } from "three/examples/jsm/interactive/SelectionBox"; import { SelectionHelper } from "./selectionHelper"; import { useFrame, useThree } from "@react-three/fiber"; -import { - useFloorItems, - useSelectedAssets, - useSimulationStates, - useSocketStore, - useToggleView, -} from "../../../../store/store"; +import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore, useToggleView, } from "../../../../store/store"; import BoundingBox from "./boundingBoxHelper"; import { toast } from "react-toastify"; // import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; @@ -23,574 +17,446 @@ import RotateControls from "./rotateControls"; import useModuleStore from "../../../../store/useModuleStore"; const SelectionControls: React.FC = () => { - const { camera, controls, gl, scene, pointer } = useThree(); - const itemsGroupRef = useRef(undefined); - const selectionGroup = useRef() as Types.RefGroup; - const { toggleView } = useToggleView(); - const { simulationStates, setSimulationStates } = useSimulationStates(); - const { selectedAssets, setSelectedAssets } = useSelectedAssets(); - const [movedObjects, setMovedObjects] = useState([]); - const [rotatedObjects, setRotatedObjects] = useState([]); - const [copiedObjects, setCopiedObjects] = useState([]); - const [pastedObjects, setpastedObjects] = useState([]); - const [duplicatedObjects, setDuplicatedObjects] = useState( - [] - ); - const boundingBoxRef = useRef(); - const { floorItems, setFloorItems } = useFloorItems(); - const { activeModule } = useModuleStore(); - const { socket } = useSocketStore(); - const selectionBox = useMemo( - () => new SelectionBox(camera, scene), - [camera, scene] - ); + const { camera, controls, gl, scene, pointer } = useThree(); + const itemsGroupRef = useRef(undefined); + const selectionGroup = useRef() as Types.RefGroup; + const { toggleView } = useToggleView(); + const { simulationStates, setSimulationStates } = useSimulationStates(); + const { selectedAssets, setSelectedAssets } = useSelectedAssets(); + const [movedObjects, setMovedObjects] = useState([]); + const [rotatedObjects, setRotatedObjects] = useState([]); + const [copiedObjects, setCopiedObjects] = useState([]); + const [pastedObjects, setpastedObjects] = useState([]); + const [duplicatedObjects, setDuplicatedObjects] = useState([]); + const boundingBoxRef = useRef(); + const { floorItems, setFloorItems } = useFloorItems(); + const { activeModule } = useModuleStore(); + const { socket } = useSocketStore(); + const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]); - useEffect(() => { - if (!camera || !scene || toggleView) return; + useEffect(() => { + if (!camera || !scene || toggleView) return; - const canvasElement = gl.domElement; - canvasElement.tabIndex = 0; + const canvasElement = gl.domElement; + canvasElement.tabIndex = 0; - const itemsGroup: any = scene.getObjectByName("itemsGroup"); - itemsGroupRef.current = itemsGroup; + const itemsGroup: any = scene.getObjectByName("itemsGroup"); + itemsGroupRef.current = itemsGroup; - let isSelecting = false; - let isRightClick = false; - let rightClickMoved = false; - let isCtrlSelecting = false; + let isSelecting = false; + let isRightClick = false; + let rightClickMoved = false; + let isCtrlSelecting = false; - const helper = new SelectionHelper(gl); + const helper = new SelectionHelper(gl); - if (!itemsGroup) { - toast.warn("itemsGroup not found in the scene."); - return; - } - - const onPointerDown = (event: PointerEvent) => { - if (event.button === 2) { - isRightClick = true; - rightClickMoved = false; - } else if (event.button === 0) { - isSelecting = false; - isCtrlSelecting = event.ctrlKey; - if (event.ctrlKey && duplicatedObjects.length === 0) { - if (controls) (controls as any).enabled = false; - selectionBox.startPoint.set(pointer.x, pointer.y, 0); + if (!itemsGroup) { + toast.warn("itemsGroup not found in the scene."); + return; } - } - }; - const onPointerMove = (event: PointerEvent) => { - if (isRightClick) { - rightClickMoved = true; - } - isSelecting = true; - if ( - helper.isDown && - event.ctrlKey && - duplicatedObjects.length === 0 && - isCtrlSelecting - ) { - selectionBox.endPoint.set(pointer.x, pointer.y, 0); - } - }; - - const onPointerUp = (event: PointerEvent) => { - if (event.button === 2) { - isRightClick = false; - if (!rightClickMoved) { - clearSelection(); - } - return; - } - - if (isSelecting && isCtrlSelecting) { - isCtrlSelecting = false; - isSelecting = false; - if (event.ctrlKey && duplicatedObjects.length === 0) { - selectAssets(); - } - } else if ( - !isSelecting && - selectedAssets.length > 0 && - ((pastedObjects.length === 0 && - duplicatedObjects.length === 0 && - movedObjects.length === 0 && - rotatedObjects.length === 0) || - event.button !== 0) - ) { - clearSelection(); - helper.enabled = true; - isCtrlSelecting = false; - } - }; - - const onKeyDown = (event: KeyboardEvent) => { - if (movedObjects.length > 0 || rotatedObjects.length > 0) return; - if (event.key.toLowerCase() === "escape") { - event.preventDefault(); - clearSelection(); - } - if (event.key.toLowerCase() === "delete") { - event.preventDefault(); - deleteSelection(); - } - }; - - const onContextMenu = (event: MouseEvent) => { - event.preventDefault(); - if (!rightClickMoved) { - clearSelection(); - } - }; - - if (!toggleView && activeModule === "builder") { - helper.enabled = true; - if (duplicatedObjects.length === 0 && pastedObjects.length === 0) { - canvasElement.addEventListener("pointerdown", onPointerDown); - canvasElement.addEventListener("pointermove", onPointerMove); - canvasElement.addEventListener("pointerup", onPointerUp); - } else { - helper.enabled = false; - helper.dispose(); - } - canvasElement.addEventListener("contextmenu", onContextMenu); - canvasElement.addEventListener("keydown", onKeyDown); - } else { - helper.enabled = false; - helper.dispose(); - } - - return () => { - canvasElement.removeEventListener("pointerdown", onPointerDown); - canvasElement.removeEventListener("pointermove", onPointerMove); - canvasElement.removeEventListener("contextmenu", onContextMenu); - canvasElement.removeEventListener("pointerup", onPointerUp); - canvasElement.removeEventListener("keydown", onKeyDown); - helper.enabled = false; - helper.dispose(); - }; - }, [ - camera, - controls, - scene, - toggleView, - selectedAssets, - copiedObjects, - pastedObjects, - duplicatedObjects, - movedObjects, - socket, - floorItems, - rotatedObjects, - activeModule, - ]); - - useEffect(() => { - if (activeModule !== "builder") { - clearSelection(); - } - }, [activeModule]); - - useFrame(() => { - if ( - pastedObjects.length === 0 && - duplicatedObjects.length === 0 && - movedObjects.length === 0 && - rotatedObjects.length === 0 - ) { - selectionGroup.current.position.set(0, 0, 0); - } - }); - - const selectAssets = () => { - selectionBox.endPoint.set(pointer.x, pointer.y, 0); - if (controls) (controls as any).enabled = true; - - let selectedObjects = selectionBox.select(); - let Objects = new Set(); - - selectedObjects.map((object) => { - let currentObject: THREE.Object3D | null = object; - while (currentObject) { - if (currentObject.userData.modelId) { - Objects.add(currentObject); - break; - } - currentObject = currentObject.parent || null; - } - }); - - if (Objects.size === 0) { - clearSelection(); - return; - } - - const updatedSelections = new Set(selectedAssets); - Objects.forEach((obj) => { - updatedSelections.has(obj) - ? updatedSelections.delete(obj) - : updatedSelections.add(obj); - }); - - const selected = Array.from(updatedSelections); - - setSelectedAssets(selected); - }; - - const clearSelection = () => { - selectionGroup.current.children = []; - selectionGroup.current.position.set(0, 0, 0); - selectionGroup.current.rotation.set(0, 0, 0); - setpastedObjects([]); - setDuplicatedObjects([]); - setSelectedAssets([]); - }; - const updateBackend = async ( - updatedPaths: ( - | SimulationTypes.ConveyorEventsSchema - | SimulationTypes.VehicleEventsSchema - | SimulationTypes.StaticMachineEventsSchema - | SimulationTypes.ArmBotEventsSchema - )[] - ) => { - if (updatedPaths.length === 0) return; - const email = localStorage.getItem("email"); - const organization = email ? email.split("@")[1].split(".")[0] : ""; - - updatedPaths.forEach(async (updatedPath) => { - if (updatedPath.type === "Conveyor") { - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { - type: "Conveyor", - points: updatedPath.points, - speed: updatedPath.speed, - }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - } else if (updatedPath.type === "Vehicle") { - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "Vehicle", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "Vehicle", points: updatedPath.points }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - } else if (updatedPath.type === "StaticMachine") { - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "StaticMachine", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "StaticMachine", points: updatedPath.points }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - } else if (updatedPath.type === "ArmBot") { - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "ArmBot", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "ArmBot", points: updatedPath.points }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - } - }); - }; - - const removeConnections = (deletedModelUUIDs: string[]) => { - const updatedStates = simulationStates.map((state) => { - // Handle Conveyor - if (state.type === "Conveyor") { - const updatedConveyor: SimulationTypes.ConveyorEventsSchema = { - ...state, - points: state.points.map((point) => { - return { - ...point, - connections: { - ...point.connections, - targets: point.connections.targets.filter( - (target) => !deletedModelUUIDs.includes(target.modelUUID) - ), - }, - }; - }), - }; - return updatedConveyor; - } - - // Handle Vehicle - else if (state.type === "Vehicle") { - const updatedVehicle: SimulationTypes.VehicleEventsSchema = { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter( - (target) => !deletedModelUUIDs.includes(target.modelUUID) - ), - }, - }, - }; - return updatedVehicle; - } - - // Handle StaticMachine - else if (state.type === "StaticMachine") { - const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = - { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter( - (target) => !deletedModelUUIDs.includes(target.modelUUID) - ), - }, - }, - }; - return updatedStaticMachine; - } - - // Handle ArmBot - else if (state.type === "ArmBot") { - const updatedArmBot: SimulationTypes.ArmBotEventsSchema = { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - - targets: state.points.connections.targets.filter( - (target: any) => !deletedModelUUIDs.includes(target.modelUUID) - ), - }, - actions: { - ...state.points.actions, - processes: (state.points.actions.processes = - state.points.actions.processes?.filter((process) => { - const matchedStates = simulationStates.filter((s) => - deletedModelUUIDs.includes(s.modeluuid) - ); - - if (matchedStates.length > 0) { - if (matchedStates[0]?.type === "StaticMachine") { - const trigPoints = matchedStates[0]?.points; - - return !( - process.triggerId === trigPoints?.triggers?.uuid - ); - } else if (matchedStates[0]?.type === "Conveyor") { - const trigPoints = matchedStates[0]?.points; - - if (Array.isArray(trigPoints)) { - const nonEmptyTriggers = trigPoints.filter( - (point) => - point && point.triggers && point.triggers.length > 0 - ); - - const allTriggerUUIDs = nonEmptyTriggers - .flatMap((point) => point.triggers) - .map((trigger) => trigger.uuid); - - return !allTriggerUUIDs.includes(process.triggerId); - } - } - } - return true; - })), - }, - }, - }; - return updatedArmBot; - } - - return state; - }); - - const filteredStates = updatedStates.filter( - (state) => !deletedModelUUIDs.includes(state.modeluuid) - ); - - updateBackend(filteredStates); - setSimulationStates(filteredStates); - }; - - const deleteSelection = () => { - if (selectedAssets.length > 0 && duplicatedObjects.length === 0) { - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; - - const storedItems = JSON.parse( - localStorage.getItem("FloorItems") || "[]" - ); - const selectedUUIDs = selectedAssets.map( - (mesh: THREE.Object3D) => mesh.uuid - ); - - const updatedStoredItems = storedItems.filter( - (item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid) - ); - localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); - - selectedAssets.forEach((selectedMesh: THREE.Object3D) => { - //REST - - // const response = await deleteFloorItem(organization, selectedMesh.uuid, selectedMesh.userData.name); - - //SOCKET - - const data = { - organization: organization, - modeluuid: selectedMesh.uuid, - modelname: selectedMesh.userData.name, - socketId: socket.id, - }; - - socket.emit("v2:model-asset:delete", data); - - selectedMesh.traverse((child: THREE.Object3D) => { - if (child instanceof THREE.Mesh) { - if (child.geometry) child.geometry.dispose(); - if (Array.isArray(child.material)) { - child.material.forEach((material) => { - if (material.map) material.map.dispose(); - material.dispose(); - }); - } else if (child.material) { - if (child.material.map) child.material.map.dispose(); - child.material.dispose(); + const onPointerDown = (event: PointerEvent) => { + if (event.button === 2) { + isRightClick = true; + rightClickMoved = false; + } else if (event.button === 0) { + isSelecting = false; + isCtrlSelecting = event.ctrlKey; + if (event.ctrlKey && duplicatedObjects.length === 0) { + if (controls) (controls as any).enabled = false; + selectionBox.startPoint.set(pointer.x, pointer.y, 0); + } + } + }; + + const onPointerMove = (event: PointerEvent) => { + if (isRightClick) { + rightClickMoved = true; + } + isSelecting = true; + if (helper.isDown && event.ctrlKey && duplicatedObjects.length === 0 && isCtrlSelecting) { + selectionBox.endPoint.set(pointer.x, pointer.y, 0); + } + }; + + const onPointerUp = (event: PointerEvent) => { + if (event.button === 2) { + isRightClick = false; + if (!rightClickMoved) { + clearSelection(); + } + return; + } + + if (isSelecting && isCtrlSelecting) { + isCtrlSelecting = false; + isSelecting = false; + if (event.ctrlKey && duplicatedObjects.length === 0) { + selectAssets(); + } + } else if (!isSelecting && selectedAssets.length > 0 && ((pastedObjects.length === 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) || event.button !== 0)) { + clearSelection(); + helper.enabled = true; + isCtrlSelecting = false; + } + }; + + const onKeyDown = (event: KeyboardEvent) => { + if (movedObjects.length > 0 || rotatedObjects.length > 0) return; + if (event.key.toLowerCase() === "escape") { + event.preventDefault(); + clearSelection(); + } + if (event.key.toLowerCase() === "delete") { + event.preventDefault(); + deleteSelection(); + } + }; + + const onContextMenu = (event: MouseEvent) => { + event.preventDefault(); + if (!rightClickMoved) { + clearSelection(); + } + }; + + if (!toggleView && activeModule === "builder") { + helper.enabled = true; + if (duplicatedObjects.length === 0 && pastedObjects.length === 0) { + canvasElement.addEventListener("pointerdown", onPointerDown); + canvasElement.addEventListener("pointermove", onPointerMove); + canvasElement.addEventListener("pointerup", onPointerUp); + } else { + helper.enabled = false; + helper.dispose(); + } + canvasElement.addEventListener("contextmenu", onContextMenu); + canvasElement.addEventListener("keydown", onKeyDown); + } else { + helper.enabled = false; + helper.dispose(); + } + + return () => { + canvasElement.removeEventListener("pointerdown", onPointerDown); + canvasElement.removeEventListener("pointermove", onPointerMove); + canvasElement.removeEventListener("contextmenu", onContextMenu); + canvasElement.removeEventListener("pointerup", onPointerUp); + canvasElement.removeEventListener("keydown", onKeyDown); + helper.enabled = false; + helper.dispose(); + }; + }, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, floorItems, rotatedObjects, activeModule,]); + + useEffect(() => { + if (activeModule !== "builder") { + clearSelection(); + } + }, [activeModule]); + + useFrame(() => { + if (pastedObjects.length === 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { + selectionGroup.current.position.set(0, 0, 0); + } + }); + + const selectAssets = () => { + selectionBox.endPoint.set(pointer.x, pointer.y, 0); + if (controls) (controls as any).enabled = true; + + let selectedObjects = selectionBox.select(); + let Objects = new Set(); + + selectedObjects.map((object) => { + let currentObject: THREE.Object3D | null = object; + while (currentObject) { + if (currentObject.userData.modelId) { + Objects.add(currentObject); + break; + } + currentObject = currentObject.parent || null; } - } }); - setSimulationStates( - ( - prevEvents: ( - | SimulationTypes.ConveyorEventsSchema - | SimulationTypes.VehicleEventsSchema - | SimulationTypes.StaticMachineEventsSchema - | SimulationTypes.ArmBotEventsSchema - )[] - ) => { - const updatedEvents = (prevEvents || []).filter( - (event) => event.modeluuid !== selectedMesh.uuid - ); - return updatedEvents; - } - ); + if (Objects.size === 0) { + clearSelection(); + return; + } - itemsGroupRef.current?.remove(selectedMesh); - }); + const updatedSelections = new Set(selectedAssets); + Objects.forEach((obj) => { updatedSelections.has(obj) ? updatedSelections.delete(obj) : updatedSelections.add(obj); }); - const allUUIDs = selectedAssets.map((val: any) => val.uuid); - removeConnections(allUUIDs); + const selected = Array.from(updatedSelections); - const updatedItems = floorItems.filter( - (item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid) - ); - setFloorItems(updatedItems); - } - toast.success("Selected models removed!"); - clearSelection(); - }; + setSelectedAssets(selected); + }; - return ( - <> - - - - - + const clearSelection = () => { + selectionGroup.current.children = []; + selectionGroup.current.position.set(0, 0, 0); + selectionGroup.current.rotation.set(0, 0, 0); + setpastedObjects([]); + setDuplicatedObjects([]); + setSelectedAssets([]); + }; + const updateBackend = async (updatedPaths: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { + if (updatedPaths.length === 0) return; + const email = localStorage.getItem("email"); + const organization = email ? email.split("@")[1].split(".")[0] : ""; - + updatedPaths.forEach(async (updatedPath) => { + if (updatedPath.type === "Conveyor") { + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed } + // ); - + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { + type: "Conveyor", + points: updatedPath.points, + speed: updatedPath.speed, + }, + }; - + socket.emit("v2:model-asset:updateEventData", data); + } else if (updatedPath.type === "Vehicle") { + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "Vehicle", points: updatedPath.points } + // ); - - - ); + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { type: "Vehicle", points: updatedPath.points }, + }; + + socket.emit("v2:model-asset:updateEventData", data); + } else if (updatedPath.type === "StaticMachine") { + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "StaticMachine", points: updatedPath.points } + // ); + + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { type: "StaticMachine", points: updatedPath.points }, + }; + + socket.emit("v2:model-asset:updateEventData", data); + } else if (updatedPath.type === "ArmBot") { + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "ArmBot", points: updatedPath.points } + // ); + + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { type: "ArmBot", points: updatedPath.points }, + }; + + socket.emit("v2:model-asset:updateEventData", data); + } + }); + }; + + const removeConnections = (deletedModelUUIDs: string[]) => { + const updatedStates = simulationStates.map((state) => { + // Handle Conveyor + if (state.type === "Conveyor") { + const updatedConveyor: SimulationTypes.ConveyorEventsSchema = { + ...state, + points: state.points.map((point) => { + return { + ...point, + connections: { + ...point.connections, + targets: point.connections.targets.filter( + (target) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + }; + }), + }; + return updatedConveyor; + } + + // Handle Vehicle + else if (state.type === "Vehicle") { + const updatedVehicle: SimulationTypes.VehicleEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter( + (target) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + }, + }; + return updatedVehicle; + } + + // Handle StaticMachine + else if (state.type === "StaticMachine") { + const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = + { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter( + (target) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + }, + }; + return updatedStaticMachine; + } + + // Handle ArmBot + else if (state.type === "ArmBot") { + const updatedArmBot: SimulationTypes.ArmBotEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + + targets: state.points.connections.targets.filter( + (target: any) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + actions: { + ...state.points.actions, + processes: (state.points.actions.processes = + state.points.actions.processes?.filter((process) => { + const matchedStates = simulationStates.filter((s) => deletedModelUUIDs.includes(s.modeluuid)); + + if (matchedStates.length > 0) { + if (matchedStates[0]?.type === "StaticMachine") { + const trigPoints = matchedStates[0]?.points; + + return !( + process.triggerId === trigPoints?.triggers?.uuid + ); + } else if (matchedStates[0]?.type === "Conveyor") { + const trigPoints = matchedStates[0]?.points; + + if (Array.isArray(trigPoints)) { + const nonEmptyTriggers = trigPoints.filter((point) => point && point.triggers && point.triggers.length > 0); + + const allTriggerUUIDs = nonEmptyTriggers.flatMap((point) => point.triggers).map((trigger) => trigger.uuid); + + return !allTriggerUUIDs.includes(process.triggerId); + } + } + } + return true; + })), + }, + }, + }; + return updatedArmBot; + } + + return state; + }); + + const filteredStates = updatedStates.filter((state) => !deletedModelUUIDs.includes(state.modeluuid)); + + updateBackend(filteredStates); + setSimulationStates(filteredStates); + }; + + const deleteSelection = () => { + if (selectedAssets.length > 0 && duplicatedObjects.length === 0) { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; + + const storedItems = JSON.parse(localStorage.getItem("FloorItems") || "[]"); + const selectedUUIDs = selectedAssets.map((mesh: THREE.Object3D) => mesh.uuid); + + const updatedStoredItems = storedItems.filter((item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid)); + localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems)); + + selectedAssets.forEach((selectedMesh: THREE.Object3D) => { + //REST + + // const response = await deleteFloorItem(organization, selectedMesh.uuid, selectedMesh.userData.name); + + //SOCKET + + const data = { + organization: organization, + modeluuid: selectedMesh.uuid, + modelname: selectedMesh.userData.name, + socketId: socket.id, + }; + + socket.emit("v2:model-asset:delete", data); + + selectedMesh.traverse((child: THREE.Object3D) => { + if (child instanceof THREE.Mesh) { + if (child.geometry) child.geometry.dispose(); + if (Array.isArray(child.material)) { + child.material.forEach((material) => { + if (material.map) material.map.dispose(); + material.dispose(); + }); + } else if (child.material) { + if (child.material.map) child.material.map.dispose(); + child.material.dispose(); + } + } + }); + + setSimulationStates((prevEvents: (| SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { + const updatedEvents = (prevEvents || []).filter((event) => event.modeluuid !== selectedMesh.uuid); + return updatedEvents; + }); + + itemsGroupRef.current?.remove(selectedMesh); + }); + + const allUUIDs = selectedAssets.map((val: any) => val.uuid); + removeConnections(allUUIDs); + + const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid)); + setFloorItems(updatedItems); + } + toast.success("Selected models removed!"); + clearSelection(); + }; + + return ( + <> + + + + + + + + + + + + + + + ); }; export default SelectionControls; diff --git a/app/src/modules/simulation/path/pathConnector.tsx b/app/src/modules/simulation/path/pathConnector.tsx index 9a49f39..4ca12a2 100644 --- a/app/src/modules/simulation/path/pathConnector.tsx +++ b/app/src/modules/simulation/path/pathConnector.tsx @@ -4,1616 +4,1280 @@ import * as THREE from "three"; import * as Types from "../../../types/world/worldTypes"; import * as SimulationTypes from "../../../types/simulationTypes"; import { QuadraticBezierLine } from "@react-three/drei"; -import { - useDeleteTool, - useIsConnecting, - useRenderDistance, - useSimulationStates, - useSocketStore, -} from "../../../store/store"; +import { useDeleteTool, useIsConnecting, useRenderDistance, useSimulationStates, useSocketStore, } from "../../../store/store"; import useModuleStore from "../../../store/useModuleStore"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; import { setEventApi } from "../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; -function PathConnector({ - pathsGroupRef, -}: { - pathsGroupRef: React.MutableRefObject; -}) { - const { activeModule } = useModuleStore(); - const { gl, raycaster, scene, pointer, camera } = useThree(); - const { deleteTool } = useDeleteTool(); - const { renderDistance } = useRenderDistance(); - const { setIsConnecting } = useIsConnecting(); - const { simulationStates, setSimulationStates } = useSimulationStates(); - const { isPlaying } = usePlayButtonStore(); - const { socket } = useSocketStore(); - const groupRefs = useRef<{ [key: string]: any }>({}); +function PathConnector({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObject; }) { + const { activeModule } = useModuleStore(); + const { gl, raycaster, scene, pointer, camera } = useThree(); + const { deleteTool } = useDeleteTool(); + const { renderDistance } = useRenderDistance(); + const { setIsConnecting } = useIsConnecting(); + const { simulationStates, setSimulationStates } = useSimulationStates(); + const { isPlaying } = usePlayButtonStore(); + const { socket } = useSocketStore(); + const groupRefs = useRef<{ [key: string]: any }>({}); - const [firstSelected, setFirstSelected] = useState<{ - modelUUID: string; - sphereUUID: string; - position: THREE.Vector3; - isCorner: boolean; - } | null>(null); - const [currentLine, setCurrentLine] = useState<{ - start: THREE.Vector3; - end: THREE.Vector3; - mid: THREE.Vector3; - } | null>(null); - const [helperlineColor, setHelperLineColor] = useState("red"); - const [hoveredLineKey, setHoveredLineKey] = useState(null); + const [firstSelected, setFirstSelected] = useState<{ modelUUID: string; sphereUUID: string; position: THREE.Vector3; isCorner: boolean; } | null>(null); + const [currentLine, setCurrentLine] = useState<{ start: THREE.Vector3; end: THREE.Vector3; mid: THREE.Vector3; } | null>(null); + const [helperlineColor, setHelperLineColor] = useState("red"); + const [hoveredLineKey, setHoveredLineKey] = useState(null); - const updatePathConnections = ( - fromModelUUID: string, - fromPointUUID: string, - toModelUUID: string, - toPointUUID: string - ) => { - const updatedPaths = simulationStates.map((path) => { - if (path.type === "Conveyor") { - // Handle outgoing connections from Conveyor - if (path.modeluuid === fromModelUUID) { - return { - ...path, - points: path.points.map((point) => { - if (point.uuid === fromPointUUID) { - const newTarget = { - modelUUID: toModelUUID, - pointUUID: toPointUUID, - }; - const existingTargets = point.connections.targets || []; + const updatePathConnections = (fromModelUUID: string, fromPointUUID: string, toModelUUID: string, toPointUUID: string) => { + const updatedPaths = simulationStates.map((path) => { + if (path.type === "Conveyor") { + // Handle outgoing connections from Conveyor + if (path.modeluuid === fromModelUUID) { + return { + ...path, + points: path.points.map((point) => { + if (point.uuid === fromPointUUID) { + const newTarget = { + modelUUID: toModelUUID, + pointUUID: toPointUUID, + }; + const existingTargets = point.connections.targets || []; - if ( - !existingTargets.some( - (target) => - target.modelUUID === newTarget.modelUUID && - target.pointUUID === newTarget.pointUUID - ) - ) { - return { - ...point, - connections: { - ...point.connections, - targets: [...existingTargets, newTarget], - }, - }; + if (!existingTargets.some((target) => target.modelUUID === newTarget.modelUUID && target.pointUUID === newTarget.pointUUID)) { + return { + ...point, + connections: { + ...point.connections, + targets: [...existingTargets, newTarget], + }, + }; + } + } + return point; + }), + }; } - } - return point; - }), - }; - } - // Handle incoming connections to Conveyor - else if (path.modeluuid === toModelUUID) { - return { - ...path, - points: path.points.map((point) => { - if (point.uuid === toPointUUID) { - const reverseTarget = { - modelUUID: fromModelUUID, - pointUUID: fromPointUUID, - }; - const existingTargets = point.connections.targets || []; + // Handle incoming connections to Conveyor + else if (path.modeluuid === toModelUUID) { + return { + ...path, + points: path.points.map((point) => { + if (point.uuid === toPointUUID) { + const reverseTarget = { + modelUUID: fromModelUUID, + pointUUID: fromPointUUID, + }; + const existingTargets = point.connections.targets || []; - if ( - !existingTargets.some( - (target) => - target.modelUUID === reverseTarget.modelUUID && - target.pointUUID === reverseTarget.pointUUID - ) - ) { - return { - ...point, - connections: { - ...point.connections, - targets: [...existingTargets, reverseTarget], - }, - }; + if (!existingTargets.some((target) => target.modelUUID === reverseTarget.modelUUID && target.pointUUID === reverseTarget.pointUUID)) { + return { + ...point, + connections: { + ...point.connections, + targets: [...existingTargets, reverseTarget], + }, + }; + } + } + return point; + }), + }; } - } - return point; - }), - }; - } - } else if (path.type === "Vehicle") { - // Handle outgoing connections from Vehicle - if ( - path.modeluuid === fromModelUUID && - path.points.uuid === fromPointUUID - ) { - const newTarget = { - modelUUID: toModelUUID, - pointUUID: toPointUUID, - }; - const existingTargets = path.points.connections.targets || []; + } else if (path.type === "Vehicle") { + // Handle outgoing connections from Vehicle + if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) { + const newTarget = { + modelUUID: toModelUUID, + pointUUID: toPointUUID, + }; + const existingTargets = path.points.connections.targets || []; - // Check if target is a Conveyor - const toPath = simulationStates.find( - (p) => p.modeluuid === toModelUUID - ); - if (toPath?.type !== "Conveyor") { - console.log("Vehicle can only connect to Conveyors"); - return path; - } - - // Check if already has a connection - if (existingTargets.length >= 1) { - console.log("Vehicle can have only one connection"); - return path; - } - - if ( - !existingTargets.some( - (target) => - target.modelUUID === newTarget.modelUUID && - target.pointUUID === newTarget.pointUUID - ) - ) { - return { - ...path, - points: { - ...path.points, - connections: { - ...path.points.connections, - targets: [...existingTargets, newTarget], - }, - }, - }; - } - } - // Handle incoming connections to Vehicle - else if ( - path.modeluuid === toModelUUID && - path.points.uuid === toPointUUID - ) { - const reverseTarget = { - modelUUID: fromModelUUID, - pointUUID: fromPointUUID, - }; - const existingTargets = path.points.connections.targets || []; - - // Check if source is a Conveyor - const fromPath = simulationStates.find( - (p) => p.modeluuid === fromModelUUID - ); - if (fromPath?.type !== "Conveyor") { - console.log("Vehicle can only connect to Conveyors"); - return path; - } - - // Check if already has a connection - if (existingTargets.length >= 1) { - console.log("Vehicle can have only one connection"); - return path; - } - - if ( - !existingTargets.some( - (target) => - target.modelUUID === reverseTarget.modelUUID && - target.pointUUID === reverseTarget.pointUUID - ) - ) { - return { - ...path, - points: { - ...path.points, - connections: { - ...path.points.connections, - targets: [...existingTargets, reverseTarget], - }, - }, - }; - } - } - return path; - } else if (path.type === "StaticMachine") { - // Handle outgoing connections from StaticMachine - if ( - path.modeluuid === fromModelUUID && - path.points.uuid === fromPointUUID - ) { - const newTarget = { - modelUUID: toModelUUID, - pointUUID: toPointUUID, - }; - - // Ensure target is an ArmBot - const toPath = simulationStates.find( - (p) => p.modeluuid === toModelUUID - ); - if (toPath?.type !== "ArmBot") { - console.log("StaticMachine can only connect to ArmBot"); - return path; - } - - const existingTargets = path.points.connections.targets || []; - - // Allow only one connection - if (existingTargets.length >= 1) { - console.log("StaticMachine can only have one connection"); - return path; - } - - if ( - !existingTargets.some( - (target) => - target.modelUUID === newTarget.modelUUID && - target.pointUUID === newTarget.pointUUID - ) - ) { - return { - ...path, - points: { - ...path.points, - connections: { - ...path.points.connections, - targets: [...existingTargets, newTarget], - }, - }, - }; - } - } - - // Handle incoming connections to StaticMachine - else if ( - path.modeluuid === toModelUUID && - path.points.uuid === toPointUUID - ) { - const reverseTarget = { - modelUUID: fromModelUUID, - pointUUID: fromPointUUID, - }; - - const fromPath = simulationStates.find( - (p) => p.modeluuid === fromModelUUID - ); - if (fromPath?.type !== "ArmBot") { - console.log("StaticMachine can only be connected from ArmBot"); - return path; - } - - const existingTargets = path.points.connections.targets || []; - - if (existingTargets.length >= 1) { - console.log("StaticMachine can only have one connection"); - return path; - } - - if ( - !existingTargets.some( - (target) => - target.modelUUID === reverseTarget.modelUUID && - target.pointUUID === reverseTarget.pointUUID - ) - ) { - return { - ...path, - points: { - ...path.points, - connections: { - ...path.points.connections, - targets: [...existingTargets, reverseTarget], - }, - }, - }; - } - } - return path; - } else if (path.type === "ArmBot") { - // Handle outgoing connections from ArmBot - if ( - path.modeluuid === fromModelUUID && - path.points.uuid === fromPointUUID - ) { - const newTarget = { - modelUUID: toModelUUID, - pointUUID: toPointUUID, - }; - - const toPath = simulationStates.find( - (p) => p.modeluuid === toModelUUID - ); - if (!toPath) return path; - - const existingTargets = path.points.connections.targets || []; - - // Check if connecting to a StaticMachine and already connected to one - const alreadyConnectedToStatic = existingTargets.some((target) => { - const targetPath = simulationStates.find( - (p) => p.modeluuid === target.modelUUID - ); - return targetPath?.type === "StaticMachine"; - }); - - if (toPath.type === "StaticMachine") { - if (alreadyConnectedToStatic) { - console.log("ArmBot can only connect to one StaticMachine"); - return path; - } - } - - if ( - !existingTargets.some( - (target) => - target.modelUUID === newTarget.modelUUID && - target.pointUUID === newTarget.pointUUID - ) - ) { - return { - ...path, - points: { - ...path.points, - connections: { - ...path.points.connections, - targets: [...existingTargets, newTarget], - }, - }, - }; - } - } - - // Handle incoming connections to ArmBot - else if ( - path.modeluuid === toModelUUID && - path.points.uuid === toPointUUID - ) { - const reverseTarget = { - modelUUID: fromModelUUID, - pointUUID: fromPointUUID, - }; - - const fromPath = simulationStates.find( - (p) => p.modeluuid === fromModelUUID - ); - if (!fromPath) return path; - - const existingTargets = path.points.connections.targets || []; - - const alreadyConnectedFromStatic = existingTargets.some((target) => { - const targetPath = simulationStates.find( - (p) => p.modeluuid === target.modelUUID - ); - return targetPath?.type === "StaticMachine"; - }); - - if (fromPath.type === "StaticMachine") { - if (alreadyConnectedFromStatic) { - console.log( - "ArmBot can only be connected from one StaticMachine" - ); - return path; - } - } - - if ( - !existingTargets.some( - (target) => - target.modelUUID === reverseTarget.modelUUID && - target.pointUUID === reverseTarget.pointUUID - ) - ) { - return { - ...path, - points: { - ...path.points, - connections: { - ...path.points.connections, - targets: [...existingTargets, reverseTarget], - }, - }, - }; - } - } - return path; - } - - return path; - }); - - setSimulationStates(updatedPaths); - - const updatedPathDetails = updatedPaths.filter( - (path) => - path.modeluuid === fromModelUUID || path.modeluuid === toModelUUID - ); - - updateBackend(updatedPathDetails); - }; - - const updateBackend = async ( - updatedPaths: ( - | SimulationTypes.ConveyorEventsSchema - | SimulationTypes.VehicleEventsSchema - | SimulationTypes.StaticMachineEventsSchema - | SimulationTypes.ArmBotEventsSchema - )[] - ) => { - if (updatedPaths.length === 0) return; - const email = localStorage.getItem("email"); - const organization = email ? email.split("@")[1].split(".")[0] : ""; - - updatedPaths.forEach(async (updatedPath) => { - if (updatedPath.type === "Conveyor") { - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { - type: "Conveyor", - points: updatedPath.points, - speed: updatedPath.speed, - }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - } else if (updatedPath.type === "Vehicle") { - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "Vehicle", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "Vehicle", points: updatedPath.points }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - } else if (updatedPath.type === "StaticMachine") { - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "StaticMachine", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "StaticMachine", points: updatedPath.points }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - } else if (updatedPath.type === "ArmBot") { - // await setEventApi( - // organization, - // updatedPath.modeluuid, - // { type: "ArmBot", points: updatedPath.points } - // ); - - const data = { - organization: organization, - modeluuid: updatedPath.modeluuid, - eventData: { type: "ArmBot", points: updatedPath.points }, - }; - - socket.emit("v2:model-asset:updateEventData", data); - } - }); - }; - - const handleAddConnection = ( - fromModelUUID: string, - fromUUID: string, - toModelUUID: string, - toUUID: string - ) => { - updatePathConnections(fromModelUUID, fromUUID, toModelUUID, toUUID); - setFirstSelected(null); - setCurrentLine(null); - setIsConnecting(false); - }; - - useEffect(() => { - const canvasElement = gl.domElement; - let drag = false; - let MouseDown = false; - - const onMouseDown = () => { - MouseDown = true; - drag = false; - }; - - const onMouseUp = () => { - MouseDown = false; - }; - - const onMouseMove = () => { - if (MouseDown) { - drag = true; - } - }; - - const onContextMenu = (evt: MouseEvent) => { - evt.preventDefault(); - if (drag || evt.button === 0) return; - - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster.intersectObjects( - pathsGroupRef.current.children, - true - ); - - if (intersects.length > 0) { - const intersected = intersects[0].object; - - if (intersected.name.includes("events-sphere")) { - const modelUUID = intersected.userData.path.modeluuid; - const sphereUUID = intersected.uuid; - const worldPosition = new THREE.Vector3(); - intersected.getWorldPosition(worldPosition); - - let isStartOrEnd = false; - - if ( - intersected.userData.path.points && - intersected.userData.path.points.length > 1 - ) { - 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.points) { - isStartOrEnd = sphereUUID === intersected.userData.path.points.uuid; - } - - if (modelUUID) { - const firstPath = simulationStates.find( - (p) => p.modeluuid === firstSelected?.modelUUID - ); - const secondPath = simulationStates.find( - (p) => p.modeluuid === modelUUID - ); - - // Prevent vehicle-to-vehicle connections - if ( - firstPath && - secondPath && - firstPath.type === "Vehicle" && - secondPath.type === "Vehicle" - ) { - console.log("Cannot connect two vehicle paths together"); - return; - } - - // Prevent conveyor middle point to conveyor connections - if ( - firstPath && - secondPath && - firstPath.type === "Conveyor" && - secondPath.type === "Conveyor" && - (!firstSelected?.isCorner || !isStartOrEnd) - ) { - console.log( - "Conveyor connections must be between start/end points" - ); - return; - } - - // Check if this specific connection already exists - const isDuplicateConnection = firstSelected - ? simulationStates.some((path) => { - if (path.modeluuid === firstSelected.modelUUID) { - if (path.type === "Conveyor") { - const point = path.points.find( - (p) => p.uuid === firstSelected.sphereUUID - ); - return point?.connections.targets.some( - (t) => - t.modelUUID === modelUUID && - t.pointUUID === sphereUUID - ); - } else if (path.type === "Vehicle") { - return path.points.connections.targets.some( - (t) => - t.modelUUID === modelUUID && - t.pointUUID === sphereUUID - ); + // Check if target is a Conveyor + const toPath = simulationStates.find((p) => p.modeluuid === toModelUUID); + if (toPath?.type !== "Conveyor") { + console.log("Vehicle can only connect to Conveyors"); + return path; } - } - return false; - }) - : false; - if (isDuplicateConnection) { - console.log("These points are already connected. Ignoring."); - return; + // Check if already has a connection + if (existingTargets.length >= 1) { + console.log("Vehicle can have only one connection"); + return path; + } + + if (!existingTargets.some((target) => target.modelUUID === newTarget.modelUUID && target.pointUUID === newTarget.pointUUID)) { + return { + ...path, + points: { + ...path.points, + connections: { + ...path.points.connections, + targets: [...existingTargets, newTarget], + }, + }, + }; + } + } + // Handle incoming connections to Vehicle + else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) { + const reverseTarget = { + modelUUID: fromModelUUID, + pointUUID: fromPointUUID, + }; + const existingTargets = path.points.connections.targets || []; + + // Check if source is a Conveyor + const fromPath = simulationStates.find((p) => p.modeluuid === fromModelUUID); + if (fromPath?.type !== "Conveyor") { + console.log("Vehicle can only connect to Conveyors"); + return path; + } + + // Check if already has a connection + if (existingTargets.length >= 1) { + console.log("Vehicle can have only one connection"); + return path; + } + + if (!existingTargets.some((target) => target.modelUUID === reverseTarget.modelUUID && target.pointUUID === reverseTarget.pointUUID)) { + return { + ...path, + points: { + ...path.points, + connections: { + ...path.points.connections, + targets: [...existingTargets, reverseTarget], + }, + }, + }; + } + } + return path; + } else if (path.type === "StaticMachine") { + // Handle outgoing connections from StaticMachine + if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) { + const newTarget = { + modelUUID: toModelUUID, + pointUUID: toPointUUID, + }; + + // Ensure target is an ArmBot + const toPath = simulationStates.find((p) => p.modeluuid === toModelUUID); + if (toPath?.type !== "ArmBot") { + console.log("StaticMachine can only connect to ArmBot"); + return path; + } + + const existingTargets = path.points.connections.targets || []; + + // Allow only one connection + if (existingTargets.length >= 1) { + console.log("StaticMachine can only have one connection"); + return path; + } + + if (!existingTargets.some((target) => target.modelUUID === newTarget.modelUUID && target.pointUUID === newTarget.pointUUID)) { + return { + ...path, + points: { + ...path.points, + connections: { + ...path.points.connections, + targets: [...existingTargets, newTarget], + }, + }, + }; + } + } + + // Handle incoming connections to StaticMachine + else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) { + const reverseTarget = { + modelUUID: fromModelUUID, + pointUUID: fromPointUUID, + }; + + const fromPath = simulationStates.find((p) => p.modeluuid === fromModelUUID); + if (fromPath?.type !== "ArmBot") { + console.log("StaticMachine can only be connected from ArmBot"); + return path; + } + + const existingTargets = path.points.connections.targets || []; + + if (existingTargets.length >= 1) { + console.log("StaticMachine can only have one connection"); + return path; + } + + if (!existingTargets.some((target) => target.modelUUID === reverseTarget.modelUUID && target.pointUUID === reverseTarget.pointUUID)) { + return { + ...path, + points: { + ...path.points, + connections: { + ...path.points.connections, + targets: [...existingTargets, reverseTarget], + }, + }, + }; + } + } + return path; + } else if (path.type === "ArmBot") { + // Handle outgoing connections from ArmBot + if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) { + const newTarget = { + modelUUID: toModelUUID, + pointUUID: toPointUUID, + }; + + const toPath = simulationStates.find((p) => p.modeluuid === toModelUUID); + if (!toPath) return path; + + const existingTargets = path.points.connections.targets || []; + + // Check if connecting to a StaticMachine and already connected to one + const alreadyConnectedToStatic = existingTargets.some((target) => { + const targetPath = simulationStates.find((p) => p.modeluuid === target.modelUUID); + return targetPath?.type === "StaticMachine"; + }); + + if (toPath.type === "StaticMachine") { + if (alreadyConnectedToStatic) { + console.log("ArmBot can only connect to one StaticMachine"); + return path; + } + } + + if (!existingTargets.some((target) => target.modelUUID === newTarget.modelUUID && target.pointUUID === newTarget.pointUUID)) { + return { + ...path, + points: { + ...path.points, + connections: { + ...path.points.connections, + targets: [...existingTargets, newTarget], + }, + }, + }; + } + } + + // Handle incoming connections to ArmBot + else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) { + const reverseTarget = { + modelUUID: fromModelUUID, + pointUUID: fromPointUUID, + }; + + const fromPath = simulationStates.find((p) => p.modeluuid === fromModelUUID); + if (!fromPath) return path; + + const existingTargets = path.points.connections.targets || []; + + const alreadyConnectedFromStatic = existingTargets.some((target) => { + const targetPath = simulationStates.find((p) => p.modeluuid === target.modelUUID); + return targetPath?.type === "StaticMachine"; + }); + + if (fromPath.type === "StaticMachine") { + if (alreadyConnectedFromStatic) { + console.log( + "ArmBot can only be connected from one StaticMachine" + ); + return path; + } + } + + if (!existingTargets.some((target) => target.modelUUID === reverseTarget.modelUUID && target.pointUUID === reverseTarget.pointUUID)) { + return { + ...path, + points: { + ...path.points, + connections: { + ...path.points.connections, + targets: [...existingTargets, reverseTarget], + }, + }, + }; + } + } + return path; } - // For Vehicles, check if they're already connected to anything - if (intersected.userData.path.type === "Vehicle") { - const vehicleConnections = - intersected.userData.path.points.connections.targets.length; - if (vehicleConnections >= 1) { - console.log("Vehicle can only have one connection"); - return; - } + return path; + }); + + setSimulationStates(updatedPaths); + + const updatedPathDetails = updatedPaths.filter((path) => path.modeluuid === fromModelUUID || path.modeluuid === toModelUUID); + + updateBackend(updatedPathDetails); + }; + + const updateBackend = async (updatedPaths: (| SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { + if (updatedPaths.length === 0) return; + const email = localStorage.getItem("email"); + const organization = email ? email.split("@")[1].split(".")[0] : ""; + + updatedPaths.forEach(async (updatedPath) => { + if (updatedPath.type === "Conveyor") { + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed } + // ); + + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { + type: "Conveyor", + points: updatedPath.points, + speed: updatedPath.speed, + }, + }; + + socket.emit("v2:model-asset:updateEventData", data); + } else if (updatedPath.type === "Vehicle") { + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "Vehicle", points: updatedPath.points } + // ); + + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { type: "Vehicle", points: updatedPath.points }, + }; + + socket.emit("v2:model-asset:updateEventData", data); + } else if (updatedPath.type === "StaticMachine") { + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "StaticMachine", points: updatedPath.points } + // ); + + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { type: "StaticMachine", points: updatedPath.points }, + }; + + socket.emit("v2:model-asset:updateEventData", data); + } else if (updatedPath.type === "ArmBot") { + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "ArmBot", points: updatedPath.points } + // ); + + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { type: "ArmBot", points: updatedPath.points }, + }; + + socket.emit("v2:model-asset:updateEventData", data); } + }); + }; - // For non-Vehicle paths, check if already connected - if (intersected.userData.path.type !== "Vehicle") { - const isAlreadyConnected = simulationStates.some((path) => { - if (path.type === "Conveyor") { - return path.points.some( - (point) => - point.uuid === sphereUUID && - point.connections.targets.length > 0 - ); - } - return false; - }); - - if (isAlreadyConnected) { - console.log("Conveyor point is already connected. Ignoring."); - return; - } - } - - if (firstSelected) { - // Check if trying to connect Vehicle to non-Conveyor - if ( - (firstPath?.type === "Vehicle" && - secondPath?.type !== "Conveyor") || - (secondPath?.type === "Vehicle" && - firstPath?.type !== "Conveyor") - ) { - console.log("Vehicle can only connect to Conveyors"); - return; - } - - // Prevent same-path connections - if (firstSelected.modelUUID === modelUUID) { - console.log("Cannot connect spheres on the same path."); - return; - } - - // Check if StaticMachine is involved in the connection - if ( - (firstPath?.type === "StaticMachine" && - secondPath?.type !== "ArmBot") || - (secondPath?.type === "StaticMachine" && - firstPath?.type !== "ArmBot") - ) { - console.log("StaticMachine can only connect to ArmBot"); - return; - } - - // Check if StaticMachine already has a connection - if (firstPath?.type === "StaticMachine") { - const staticConnections = - firstPath.points.connections.targets.length; - if (staticConnections >= 1) { - console.log("StaticMachine can only have one connection"); - return; - } - } - if (secondPath?.type === "StaticMachine") { - const staticConnections = - secondPath.points.connections.targets.length; - if (staticConnections >= 1) { - console.log("StaticMachine can only have one connection"); - return; - } - } - - // Check if ArmBot is involved - if ( - (firstPath?.type === "ArmBot" && - secondPath?.type === "StaticMachine") || - (secondPath?.type === "ArmBot" && - firstPath?.type === "StaticMachine") - ) { - const armBotPath = - firstPath?.type === "ArmBot" ? firstPath : secondPath; - const staticPath = - firstPath?.type === "StaticMachine" ? firstPath : secondPath; - - const armBotConnections = - armBotPath.points.connections.targets || []; - const alreadyConnectedToStatic = armBotConnections.some( - (target) => { - const targetPath = simulationStates.find( - (p) => p.modeluuid === target.modelUUID - ); - return targetPath?.type === "StaticMachine"; - } - ); - - if (alreadyConnectedToStatic) { - console.log("ArmBot can only connect to one StaticMachine"); - return; - } - - const staticConnections = - staticPath.points.connections.targets.length; - if (staticConnections >= 1) { - console.log("StaticMachine can only have one connection"); - return; - } - } - - // Prevent ArmBot ↔ ArmBot - if ( - firstPath?.type === "ArmBot" && - secondPath?.type === "ArmBot" - ) { - console.log("Cannot connect two ArmBots together"); - return; - } - - // If one is ArmBot, ensure the other is StaticMachine or Conveyor - if ( - firstPath?.type === "ArmBot" || - secondPath?.type === "ArmBot" - ) { - const otherType = - firstPath?.type === "ArmBot" - ? secondPath?.type - : firstPath?.type; - if (otherType !== "StaticMachine" && otherType !== "Conveyor") { - console.log( - "ArmBot can only connect to Conveyors or one StaticMachine" - ); - return; - } - } - - // At least one must be start/end point - if (!firstSelected.isCorner && !isStartOrEnd) { - console.log( - "At least one of the selected spheres must be a start or end point." - ); - return; - } - - // All checks passed - make the connection - handleAddConnection( - firstSelected.modelUUID, - firstSelected.sphereUUID, - modelUUID, - sphereUUID - ); - } else { - // First selection - just store it - setFirstSelected({ - modelUUID, - sphereUUID, - position: worldPosition, - isCorner: isStartOrEnd, - }); - setIsConnecting(true); - } - } - } - } else { - // Clicked outside - cancel connection + const handleAddConnection = (fromModelUUID: string, fromUUID: string, toModelUUID: string, toUUID: string) => { + updatePathConnections(fromModelUUID, fromUUID, toModelUUID, toUUID); setFirstSelected(null); setCurrentLine(null); setIsConnecting(false); - } }; - if (activeModule === "simulation" && !deleteTool) { - canvasElement.addEventListener("mousedown", onMouseDown); - canvasElement.addEventListener("mouseup", onMouseUp); - canvasElement.addEventListener("mousemove", onMouseMove); - canvasElement.addEventListener("contextmenu", onContextMenu); - } else { - setFirstSelected(null); - setCurrentLine(null); - setIsConnecting(false); - } + useEffect(() => { + const canvasElement = gl.domElement; + let drag = false; + let MouseDown = false; - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - canvasElement.removeEventListener("contextmenu", onContextMenu); - }; - }, [camera, scene, raycaster, firstSelected, simulationStates, deleteTool]); - - useFrame(() => { - Object.values(groupRefs.current).forEach((group) => { - if (group) { - const distance = new THREE.Vector3( - ...group.position.toArray() - ).distanceTo(camera.position); - group.visible = distance <= renderDistance && !isPlaying; - } - }); - }); - - useFrame(() => { - if (firstSelected) { - 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") - ); - - let point: THREE.Vector3 | null = null; - let snappedSphere: { - sphereUUID: string; - position: THREE.Vector3; - modelUUID: string; - isCorner: boolean; - } | null = null; - let isInvalidConnection = false; - - if (intersects.length > 0) { - point = intersects[0].point; - if (point.y < 0.05) { - point = new THREE.Vector3(point.x, 0.05, point.z); - } - } - - const sphereIntersects = raycaster - .intersectObjects(pathsGroupRef.current.children, true) - .filter((obj) => obj.object.name.includes("events-sphere")); - - if (sphereIntersects.length > 0) { - const sphere = sphereIntersects[0].object; - const sphereUUID = sphere.uuid; - const spherePosition = new THREE.Vector3(); - sphere.getWorldPosition(spherePosition); - const pathData = sphere.userData.path; - const modelUUID = pathData.modeluuid; - - const firstPath = simulationStates.find( - (p) => p.modeluuid === firstSelected.modelUUID - ); - const secondPath = simulationStates.find( - (p) => p.modeluuid === modelUUID - ); - const isVehicleToVehicle = - firstPath?.type === "Vehicle" && secondPath?.type === "Vehicle"; - - // Inside the useFrame hook, where we check for snapped spheres: - const isConnectable = - (pathData.type === "Vehicle" || - pathData.type === "ArmBot" || - (pathData.points.length > 0 && - (sphereUUID === pathData.points[0].uuid || - sphereUUID === - pathData.points[pathData.points.length - 1].uuid || - (pathData.type === "Conveyor" && - firstPath?.type === "ArmBot")))) && // Allow ArmBot to connect to middle points - !isVehicleToVehicle && - !( - firstPath?.type === "Conveyor" && - pathData.type === "Conveyor" && - !firstSelected.isCorner - ); - - // Check for duplicate connection (regardless of path type) - const isDuplicateConnection = simulationStates.some((path) => { - if (path.modeluuid === firstSelected.modelUUID) { - if (path.type === "Conveyor") { - const point = path.points.find( - (p) => p.uuid === firstSelected.sphereUUID - ); - return point?.connections.targets.some( - (t) => t.modelUUID === modelUUID && t.pointUUID === sphereUUID - ); - } else if (path.type === "Vehicle") { - return path.points.connections.targets.some( - (t) => t.modelUUID === modelUUID && t.pointUUID === sphereUUID - ); - } - } - return false; - }); - - // For non-Vehicle paths, check if already connected - const isNonVehicleAlreadyConnected = - pathData.type !== "Vehicle" && - simulationStates.some((path) => { - if (path.type === "Conveyor") { - return path.points.some( - (point) => - point.uuid === sphereUUID && - point.connections.targets.length > 0 - ); - } - return false; - }); - - // Check vehicle connection rules - const isVehicleAtMaxConnections = - pathData.type === "Vehicle" && - pathData.points.connections.targets.length >= 1; - const isVehicleConnectingToNonConveyor = - (firstPath?.type === "Vehicle" && secondPath?.type !== "Conveyor") || - (secondPath?.type === "Vehicle" && firstPath?.type !== "Conveyor"); - - // Check if StaticMachine is connecting to non-ArmBot - const isStaticMachineToNonArmBot = - (firstPath?.type === "StaticMachine" && - secondPath?.type !== "ArmBot") || - (secondPath?.type === "StaticMachine" && - firstPath?.type !== "ArmBot"); - - // Check if StaticMachine already has a connection - const isStaticMachineAtMaxConnections = - (firstPath?.type === "StaticMachine" && - firstPath.points.connections.targets.length >= 1) || - (secondPath?.type === "StaticMachine" && - secondPath.points.connections.targets.length >= 1); - - // Check if ArmBot is connecting to StaticMachine - const isArmBotToStaticMachine = - (firstPath?.type === "ArmBot" && - secondPath?.type === "StaticMachine") || - (secondPath?.type === "ArmBot" && - firstPath?.type === "StaticMachine"); - - // Prevent multiple StaticMachine connections to ArmBot - let isArmBotAlreadyConnectedToStatic = false; - if (isArmBotToStaticMachine) { - const armBotPath = - firstPath?.type === "ArmBot" ? firstPath : secondPath; - isArmBotAlreadyConnectedToStatic = - armBotPath.points.connections.targets.some((target) => { - const targetPath = simulationStates.find( - (p) => p.modeluuid === target.modelUUID - ); - return targetPath?.type === "StaticMachine"; - }); - } - - // Prevent ArmBot to ArmBot - const isArmBotToArmBot = - firstPath?.type === "ArmBot" && secondPath?.type === "ArmBot"; - - // If ArmBot is involved, other must be Conveyor or StaticMachine - const isArmBotToInvalidType = - (firstPath?.type === "ArmBot" || secondPath?.type === "ArmBot") && - !( - firstPath?.type === "Conveyor" || - firstPath?.type === "StaticMachine" || - secondPath?.type === "Conveyor" || - secondPath?.type === "StaticMachine" - ); - - if ( - !isDuplicateConnection && - !isVehicleToVehicle && - !isNonVehicleAlreadyConnected && - !isVehicleAtMaxConnections && - !isVehicleConnectingToNonConveyor && - !isStaticMachineToNonArmBot && - !isStaticMachineAtMaxConnections && - !isArmBotToArmBot && - !isArmBotToInvalidType && - !isArmBotAlreadyConnectedToStatic && - firstSelected.sphereUUID !== sphereUUID && - firstSelected.modelUUID !== modelUUID && - (firstSelected.isCorner || isConnectable) && - !( - firstPath?.type === "Conveyor" && - pathData.type === "Conveyor" && - !(firstSelected.isCorner && isConnectable) - ) - ) { - snappedSphere = { - sphereUUID, - position: spherePosition, - modelUUID, - isCorner: isConnectable, - }; - } else { - isInvalidConnection = true; - } - } - - if (snappedSphere) { - point = snappedSphere.position; - } - - if (point) { - const distance = firstSelected.position.distanceTo(point); - const heightFactor = Math.max(0.5, distance * 0.2); - const midPoint = new THREE.Vector3( - (firstSelected.position.x + point.x) / 2, - Math.max(firstSelected.position.y, point.y) + heightFactor, - (firstSelected.position.z + point.z) / 2 - ); - - setCurrentLine({ - start: firstSelected.position, - end: point, - mid: midPoint, - }); - - if (sphereIntersects.length > 0) { - setHelperLineColor(isInvalidConnection ? "red" : "#6cf542"); - } else { - setHelperLineColor("yellow"); - } - } else { - setCurrentLine(null); - setIsConnecting(false); - } - } else { - setCurrentLine(null); - setIsConnecting(false); - } - }); - - const removeConnections = ( - connection1: { model: string; point: string }, - connection2: { model: string; point: string } - ) => { - const updatedStates = simulationStates.map((state) => { - // Handle Conveyor (which has multiple points) - if (state.type === "Conveyor") { - const updatedConveyor: SimulationTypes.ConveyorEventsSchema = { - ...state, - points: state.points.map((point) => { - // Check if this point is either connection1 or connection2 - if ( - (state.modeluuid === connection1.model && - point.uuid === connection1.point) || - (state.modeluuid === connection2.model && - point.uuid === connection2.point) - ) { - return { - ...point, - connections: { - ...point.connections, - targets: point.connections.targets.filter((target) => { - // Remove the target that matches the other connection - return !( - (target.modelUUID === connection1.model && - target.pointUUID === connection1.point) || - (target.modelUUID === connection2.model && - target.pointUUID === connection2.point) - ); - }), - }, - }; - } - return point; - }), + const onMouseDown = () => { + MouseDown = true; + drag = false; }; - return updatedConveyor; - } - // Handle Vehicle - else if (state.type === "Vehicle") { - if ( - (state.modeluuid === connection1.model && - state.points.uuid === connection1.point) || - (state.modeluuid === connection2.model && - state.points.uuid === connection2.point) - ) { - const updatedVehicle: SimulationTypes.VehicleEventsSchema = { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter((target) => { - return !( - (target.modelUUID === connection1.model && - target.pointUUID === connection1.point) || - (target.modelUUID === connection2.model && - target.pointUUID === connection2.point) - ); - }), - }, - // Ensure all required Vehicle point properties are included - speed: state.points.speed, - actions: state.points.actions, - }, - }; - return updatedVehicle; - } - } - // Handle StaticMachine - else if (state.type === "StaticMachine") { - if ( - (state.modeluuid === connection1.model && - state.points.uuid === connection1.point) || - (state.modeluuid === connection2.model && - state.points.uuid === connection2.point) - ) { - const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = - { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter((target) => { - return !( - (target.modelUUID === connection1.model && - target.pointUUID === connection1.point) || - (target.modelUUID === connection2.model && - target.pointUUID === connection2.point) - ); - }), - }, - // Ensure all required StaticMachine point properties are included - actions: state.points.actions, - triggers: state.points.triggers, - }, - }; - return updatedStaticMachine; - } - } - // Handle ArmBot - else if (state.type === "ArmBot") { - if ( - (state.modeluuid === connection1.model && - state.points.uuid === connection1.point) || - (state.modeluuid === connection2.model && - state.points.uuid === connection2.point) - ) { - const updatedArmBot: SimulationTypes.ArmBotEventsSchema = { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter((target) => { - return !( - (target.modelUUID === connection1.model && - target.pointUUID === connection1.point) || - (target.modelUUID === connection2.model && - target.pointUUID === connection2.point) - ); - }), - }, - actions: { - ...state.points.actions, - processes: - state.points.actions.processes?.filter((process) => { - return !( - process.startPoint === connection1.point || - process.endPoint === connection1.point || - process.startPoint === connection2.point || - process.endPoint === connection2.point - ); - }) || [], - }, - triggers: state.points.triggers, - }, - }; - return updatedArmBot; - } - } - return state; - }); - const updatedPaths = updatedStates.filter( - (state) => - state.modeluuid === connection1.model || - state.modeluuid === connection2.model - ); - - console.log("updatedPaths: ", updatedPaths); - updateBackend(updatedPaths); - - setSimulationStates(updatedStates); - }; - const removeConnection = (deletedModelUUIDs: string[]) => { - const updatedStates = simulationStates.map((state) => { - // Handle Conveyor - if (state.type === "Conveyor") { - const updatedConveyor: SimulationTypes.ConveyorEventsSchema = { - ...state, - points: state.points.map((point) => { - return { - ...point, - connections: { - ...point.connections, - targets: point.connections.targets.filter( - (target) => !deletedModelUUIDs.includes(target.modelUUID) - ), - }, - }; - }), + const onMouseUp = () => { + MouseDown = false; }; - return updatedConveyor; - } - // Handle Vehicle - else if (state.type === "Vehicle") { - const updatedVehicle: SimulationTypes.VehicleEventsSchema = { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter( - (target) => !deletedModelUUIDs.includes(target.modelUUID) - ), - }, - }, + const onMouseMove = () => { + if (MouseDown) { + drag = true; + } }; - return updatedVehicle; - } - // Handle StaticMachine - else if (state.type === "StaticMachine") { - const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = - { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, - targets: state.points.connections.targets.filter( - (target) => !deletedModelUUIDs.includes(target.modelUUID) - ), - }, - }, - }; - return updatedStaticMachine; - } + const onContextMenu = (evt: MouseEvent) => { + evt.preventDefault(); + if (drag || evt.button === 0) return; - // Handle ArmBot - else if (state.type === "ArmBot") { - const updatedArmBot: SimulationTypes.ArmBotEventsSchema = { - ...state, - points: { - ...state.points, - connections: { - ...state.points.connections, + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster.intersectObjects( + pathsGroupRef.current.children, + true + ); - targets: state.points.connections.targets.filter( - (target: any) => !deletedModelUUIDs.includes(target.modelUUID) - ), - }, - actions: { - ...state.points.actions, - processes: (state.points.actions.processes = - state.points.actions.processes?.filter((process) => { - const matchedStates = simulationStates.filter((s) => - deletedModelUUIDs.includes(s.modeluuid) - ); + if (intersects.length > 0) { + const intersected = intersects[0].object; - if (matchedStates.length > 0) { - if (matchedStates[0]?.type === "StaticMachine") { - const trigPoints = matchedStates[0]?.points; + if (intersected.name.includes("events-sphere")) { + const modelUUID = intersected.userData.path.modeluuid; + const sphereUUID = intersected.uuid; + const worldPosition = new THREE.Vector3(); + intersected.getWorldPosition(worldPosition); - return !( - process.triggerId === trigPoints?.triggers?.uuid - ); - } else if (matchedStates[0]?.type === "Conveyor") { - const trigPoints = matchedStates[0]?.points; + let isStartOrEnd = false; - if (Array.isArray(trigPoints)) { - const nonEmptyTriggers = trigPoints.filter( - (point) => - point && point.triggers && point.triggers.length > 0 - ); - - const allTriggerUUIDs = nonEmptyTriggers - .flatMap((point) => point.triggers) - .map((trigger) => trigger.uuid); - - return !allTriggerUUIDs.includes(process.triggerId); - } + if (intersected.userData.path.points && intersected.userData.path.points.length > 1) { + 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.points) { + isStartOrEnd = sphereUUID === intersected.userData.path.points.uuid; } - } - return true; - })), - }, - }, - }; - return updatedArmBot; - } - return state; + if (modelUUID) { + const firstPath = simulationStates.find((p) => p.modeluuid === firstSelected?.modelUUID); + const secondPath = simulationStates.find((p) => p.modeluuid === modelUUID); + + // Prevent vehicle-to-vehicle connections + if (firstPath && secondPath && firstPath.type === "Vehicle" && secondPath.type === "Vehicle") { + console.log("Cannot connect two vehicle paths together"); + return; + } + + // Prevent conveyor middle point to conveyor connections + if (firstPath && secondPath && firstPath.type === "Conveyor" && secondPath.type === "Conveyor" && (!firstSelected?.isCorner || !isStartOrEnd)) { + console.log("Conveyor connections must be between start/end points"); + return; + } + + // Check if this specific connection already exists + const isDuplicateConnection = firstSelected + ? simulationStates.some((path) => { + if (path.modeluuid === firstSelected.modelUUID) { + if (path.type === "Conveyor") { + const point = path.points.find( + (p) => p.uuid === firstSelected.sphereUUID + ); + return point?.connections.targets.some( + (t) => + t.modelUUID === modelUUID && + t.pointUUID === sphereUUID + ); + } else if (path.type === "Vehicle") { + return path.points.connections.targets.some( + (t) => + t.modelUUID === modelUUID && + t.pointUUID === sphereUUID + ); + } + } + return false; + }) + : false; + + if (isDuplicateConnection) { + console.log("These points are already connected. Ignoring."); + return; + } + + // For Vehicles, check if they're already connected to anything + if (intersected.userData.path.type === "Vehicle") { + const vehicleConnections = + intersected.userData.path.points.connections.targets.length; + if (vehicleConnections >= 1) { + console.log("Vehicle can only have one connection"); + return; + } + } + + // For non-Vehicle paths, check if already connected + if (intersected.userData.path.type !== "Vehicle") { + const isAlreadyConnected = simulationStates.some((path) => { + if (path.type === "Conveyor") { + return path.points.some( + (point) => + point.uuid === sphereUUID && + point.connections.targets.length > 0 + ); + } + return false; + }); + + if (isAlreadyConnected) { + console.log("Conveyor point is already connected. Ignoring."); + return; + } + } + + if (firstSelected) { + // Check if trying to connect Vehicle to non-Conveyor + if ((firstPath?.type === "Vehicle" && secondPath?.type !== "Conveyor") || (secondPath?.type === "Vehicle" && firstPath?.type !== "Conveyor")) { + console.log("Vehicle can only connect to Conveyors"); + return; + } + + // Prevent same-path connections + if (firstSelected.modelUUID === modelUUID) { + console.log("Cannot connect spheres on the same path."); + return; + } + + // Check if StaticMachine is involved in the connection + if ((firstPath?.type === "StaticMachine" && secondPath?.type !== "ArmBot") || (secondPath?.type === "StaticMachine" && firstPath?.type !== "ArmBot")) { + console.log("StaticMachine can only connect to ArmBot"); + return; + } + + // Check if StaticMachine already has a connection + if (firstPath?.type === "StaticMachine") { + const staticConnections = firstPath.points.connections.targets.length; + if (staticConnections >= 1) { + console.log("StaticMachine can only have one connection"); + return; + } + } + if (secondPath?.type === "StaticMachine") { + const staticConnections = secondPath.points.connections.targets.length; + if (staticConnections >= 1) { + console.log("StaticMachine can only have one connection"); + return; + } + } + + // Check if ArmBot is involved + if ((firstPath?.type === "ArmBot" && secondPath?.type === "StaticMachine") || (secondPath?.type === "ArmBot" && firstPath?.type === "StaticMachine")) { + const armBotPath = firstPath?.type === "ArmBot" ? firstPath : secondPath; + const staticPath = firstPath?.type === "StaticMachine" ? firstPath : secondPath; + + const armBotConnections = armBotPath.points.connections.targets || []; + const alreadyConnectedToStatic = armBotConnections.some( + (target) => { + const targetPath = simulationStates.find((p) => p.modeluuid === target.modelUUID); + return targetPath?.type === "StaticMachine"; + } + ); + + if (alreadyConnectedToStatic) { + console.log("ArmBot can only connect to one StaticMachine"); + return; + } + + const staticConnections = staticPath.points.connections.targets.length; + if (staticConnections >= 1) { + console.log("StaticMachine can only have one connection"); + return; + } + } + + // Prevent ArmBot ↔ ArmBot + if (firstPath?.type === "ArmBot" && secondPath?.type === "ArmBot") { + console.log("Cannot connect two ArmBots together"); + return; + } + + // If one is ArmBot, ensure the other is StaticMachine or Conveyor + if (firstPath?.type === "ArmBot" || secondPath?.type === "ArmBot") { + const otherType = firstPath?.type === "ArmBot" ? secondPath?.type : firstPath?.type; + if (otherType !== "StaticMachine" && otherType !== "Conveyor") { + console.log("ArmBot can only connect to Conveyors or one StaticMachine"); + return; + } + } + + // At least one must be start/end point + if (!firstSelected.isCorner && !isStartOrEnd) { + console.log("At least one of the selected spheres must be a start or end point."); + return; + } + + // All checks passed - make the connection + handleAddConnection(firstSelected.modelUUID, firstSelected.sphereUUID, modelUUID, sphereUUID); + } else { + // First selection - just store it + setFirstSelected({ modelUUID, sphereUUID, position: worldPosition, isCorner: isStartOrEnd }); + setIsConnecting(true); + } + } + } + } else { + // Clicked outside - cancel connection + setFirstSelected(null); + setCurrentLine(null); + setIsConnecting(false); + } + }; + + if (activeModule === "simulation" && !deleteTool) { + canvasElement.addEventListener("mousedown", onMouseDown); + canvasElement.addEventListener("mouseup", onMouseUp); + canvasElement.addEventListener("mousemove", onMouseMove); + canvasElement.addEventListener("contextmenu", onContextMenu); + } else { + setFirstSelected(null); + setCurrentLine(null); + setIsConnecting(false); + } + + return () => { + canvasElement.removeEventListener("mousedown", onMouseDown); + canvasElement.removeEventListener("mouseup", onMouseUp); + canvasElement.removeEventListener("mousemove", onMouseMove); + canvasElement.removeEventListener("contextmenu", onContextMenu); + }; + }, [camera, scene, raycaster, firstSelected, simulationStates, deleteTool]); + + useFrame(() => { + Object.values(groupRefs.current).forEach((group) => { + if (group) { + const distance = new THREE.Vector3(...group.position.toArray()).distanceTo(camera.position); + group.visible = distance <= renderDistance && !isPlaying; + } + }); }); - const filteredStates = updatedStates.filter( - (state) => !deletedModelUUIDs.includes(state.modeluuid) - ); + useFrame(() => { + if (firstSelected) { + 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") + ); - updateBackend(filteredStates); - setSimulationStates(filteredStates); - }; + let point: THREE.Vector3 | null = null; + let snappedSphere: { sphereUUID: string; position: THREE.Vector3; modelUUID: string; isCorner: boolean; } | null = null; + let isInvalidConnection = false; - return ( - - {simulationStates.flatMap((path) => { - if (path.type === "Conveyor") { - return path.points.flatMap((point) => - point.connections.targets.map((target, index) => { - const targetPath = simulationStates.find( - (p) => p.modeluuid === target.modelUUID - ); - if ( - targetPath?.type !== "Conveyor" && - targetPath?.type !== "ArmBot" - ) - return null; + if (intersects.length > 0) { + point = intersects[0].point; + if (point.y < 0.05) { + point = new THREE.Vector3(point.x, 0.05, point.z); + } + } - const fromSphere = pathsGroupRef.current?.getObjectByProperty( - "uuid", - point.uuid - ); - const toSphere = pathsGroupRef.current?.getObjectByProperty( - "uuid", - target.pointUUID - ); + const sphereIntersects = raycaster.intersectObjects(pathsGroupRef.current.children, true).filter((obj) => obj.object.name.includes("events-sphere")); - if (fromSphere && toSphere) { - const fromWorldPosition = new THREE.Vector3(); - const toWorldPosition = new THREE.Vector3(); - fromSphere.getWorldPosition(fromWorldPosition); - toSphere.getWorldPosition(toWorldPosition); + if (sphereIntersects.length > 0) { + const sphere = sphereIntersects[0].object; + const sphereUUID = sphere.uuid; + const spherePosition = new THREE.Vector3(); + sphere.getWorldPosition(spherePosition); + const pathData = sphere.userData.path; + const modelUUID = pathData.modeluuid; - const distance = fromWorldPosition.distanceTo(toWorldPosition); + const firstPath = simulationStates.find((p) => p.modeluuid === firstSelected.modelUUID); + const secondPath = simulationStates.find((p) => p.modeluuid === modelUUID); + const isVehicleToVehicle = firstPath?.type === "Vehicle" && secondPath?.type === "Vehicle"; + + // Inside the useFrame hook, where we check for snapped spheres: + const isConnectable = ( + pathData.type === 'Vehicle' || + pathData.type === 'ArmBot' || + (pathData.points.length > 0 && ( + sphereUUID === pathData.points[0].uuid || + sphereUUID === pathData.points[pathData.points.length - 1].uuid || + (pathData.type === 'Conveyor' && firstPath?.type === 'ArmBot') // Allow ArmBot to connect to middle points + )) + ) && + !isVehicleToVehicle && + !( + firstPath?.type === 'Conveyor' && + pathData.type === 'Conveyor' && + !firstSelected.isCorner + ); + + // Check for duplicate connection (regardless of path type) + const isDuplicateConnection = simulationStates.some(path => { + if (path.modeluuid === firstSelected.modelUUID) { + if (path.type === 'Conveyor') { + const point = path.points.find(p => p.uuid === firstSelected.sphereUUID); + return point?.connections.targets.some(t => + t.modelUUID === modelUUID && t.pointUUID === sphereUUID + ); + } else if (path.type === 'Vehicle') { + return path.points.connections.targets.some(t => + t.modelUUID === modelUUID && t.pointUUID === sphereUUID + ); + } + } + return false; + }); + + // For non-Vehicle paths, check if already connected + const isNonVehicleAlreadyConnected = pathData.type !== 'Vehicle' && + simulationStates.some(path => { + if (path.type === 'Conveyor') { + return path.points.some(point => + point.uuid === sphereUUID && + point.connections.targets.length > 0 + ); + } + return false; + }); + + // Check vehicle connection rules + const isVehicleAtMaxConnections = pathData.type === 'Vehicle' && + pathData.points.connections.targets.length >= 1; + const isVehicleConnectingToNonConveyor = + (firstPath?.type === 'Vehicle' && secondPath?.type !== 'Conveyor') || + (secondPath?.type === 'Vehicle' && firstPath?.type !== 'Conveyor'); + + // Check if StaticMachine is connecting to non-ArmBot + const isStaticMachineToNonArmBot = + (firstPath?.type === 'StaticMachine' && secondPath?.type !== 'ArmBot') || + (secondPath?.type === 'StaticMachine' && firstPath?.type !== 'ArmBot'); + + // Check if StaticMachine already has a connection + const isStaticMachineAtMaxConnections = + (firstPath?.type === 'StaticMachine' && firstPath.points.connections.targets.length >= 1) || + (secondPath?.type === 'StaticMachine' && secondPath.points.connections.targets.length >= 1); + + // Check if ArmBot is connecting to StaticMachine + const isArmBotToStaticMachine = + (firstPath?.type === 'ArmBot' && secondPath?.type === 'StaticMachine') || + (secondPath?.type === 'ArmBot' && firstPath?.type === 'StaticMachine'); + + // Prevent multiple StaticMachine connections to ArmBot + let isArmBotAlreadyConnectedToStatic = false; + if (isArmBotToStaticMachine) { + const armBotPath = firstPath?.type === 'ArmBot' ? firstPath : secondPath; + isArmBotAlreadyConnectedToStatic = armBotPath.points.connections.targets.some(target => { + const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID); + return targetPath?.type === 'StaticMachine'; + }); + } + + // Prevent ArmBot to ArmBot + const isArmBotToArmBot = firstPath?.type === 'ArmBot' && secondPath?.type === 'ArmBot'; + + // If ArmBot is involved, other must be Conveyor or StaticMachine + const isArmBotToInvalidType = (firstPath?.type === 'ArmBot' || secondPath?.type === 'ArmBot') && + !(firstPath?.type === 'Conveyor' || firstPath?.type === 'StaticMachine' || + secondPath?.type === 'Conveyor' || secondPath?.type === 'StaticMachine'); + + if ( + !isDuplicateConnection && + !isVehicleToVehicle && + !isNonVehicleAlreadyConnected && + !isVehicleAtMaxConnections && + !isVehicleConnectingToNonConveyor && + !isStaticMachineToNonArmBot && + !isStaticMachineAtMaxConnections && + !isArmBotToArmBot && + !isArmBotToInvalidType && + !isArmBotAlreadyConnectedToStatic && + firstSelected.sphereUUID !== sphereUUID && + firstSelected.modelUUID !== modelUUID && + (firstSelected.isCorner || isConnectable) && + !(firstPath?.type === 'Conveyor' && + pathData.type === 'Conveyor' && + !(firstSelected.isCorner && isConnectable)) + ) { + snappedSphere = { + sphereUUID, + position: spherePosition, + modelUUID, + isCorner: isConnectable + }; + } else { + isInvalidConnection = true; + } + } + + if (snappedSphere) { + point = snappedSphere.position; + } + + if (point) { + const distance = firstSelected.position.distanceTo(point); 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 + (firstSelected.position.x + point.x) / 2, + Math.max(firstSelected.position.y, point.y) + heightFactor, + (firstSelected.position.z + point.z) / 2 ); - return ( - - (groupRefs.current[ - `${point.uuid}-${target.pointUUID}-${index}` - ] = el!) - } - start={fromWorldPosition.toArray()} - end={toWorldPosition.toArray()} - mid={midPoint.toArray()} - color={ - deleteTool && - hoveredLineKey === - `${point.uuid}-${target.pointUUID}-${index}` - ? "red" - : targetPath?.type === "ArmBot" - ? "#42a5f5" - : "white" - } + setCurrentLine({ + start: firstSelected.position, + end: point, + mid: midPoint, + }); + + if (sphereIntersects.length > 0) { + setHelperLineColor(isInvalidConnection ? "red" : "#6cf542"); + } else { + setHelperLineColor("yellow"); + } + } else { + setCurrentLine(null); + setIsConnecting(false); + } + } else { + setCurrentLine(null); + setIsConnecting(false); + } + }); + + const removeConnection = (connection1: { model: string; point: string }, connection2: { model: string; point: string }) => { + const updatedStates = simulationStates.map((state) => { + + // Handle Conveyor (which has multiple points) + if (state.type === "Conveyor") { + const updatedConveyor: SimulationTypes.ConveyorEventsSchema = { + ...state, + points: state.points.map((point) => { + // Check if this point is either connection1 or connection2 + if ((state.modeluuid === connection1.model && point.uuid === connection1.point) || (state.modeluuid === connection2.model && point.uuid === connection2.point)) { + return { + ...point, + connections: { + ...point.connections, + targets: point.connections.targets.filter((target) => { + // Remove the target that matches the other connection + return !( + (target.modelUUID === connection1.model && + target.pointUUID === connection1.point) || + (target.modelUUID === connection2.model && + target.pointUUID === connection2.point) + ); + }), + }, + }; + } + return point; + }), + }; + return updatedConveyor; + } + + // Handle Vehicle + else if (state.type === "Vehicle") { + if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) || (state.modeluuid === connection2.model && state.points.uuid === connection2.point)) { + const updatedVehicle: SimulationTypes.VehicleEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter((target) => { + return !( + (target.modelUUID === connection1.model && + target.pointUUID === connection1.point) || + (target.modelUUID === connection2.model && + target.pointUUID === connection2.point) + ); + }), + }, + // Ensure all required Vehicle point properties are included + speed: state.points.speed, + actions: state.points.actions, + }, + }; + return updatedVehicle; + } + } + + // Handle StaticMachine + else if (state.type === "StaticMachine") { + if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) || (state.modeluuid === connection2.model && state.points.uuid === connection2.point)) { + const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = + { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter((target) => { + return !( + (target.modelUUID === connection1.model && + target.pointUUID === connection1.point) || + (target.modelUUID === connection2.model && + target.pointUUID === connection2.point) + ); + }), + }, + // Ensure all required StaticMachine point properties are included + actions: state.points.actions, + triggers: state.points.triggers, + }, + }; + return updatedStaticMachine; + } + } + + // Handle ArmBot + else if (state.type === "ArmBot") { + if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) || (state.modeluuid === connection2.model && state.points.uuid === connection2.point)) { + const updatedArmBot: SimulationTypes.ArmBotEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter((target) => { + return !( + (target.modelUUID === connection1.model && + target.pointUUID === connection1.point) || + (target.modelUUID === connection2.model && + target.pointUUID === connection2.point) + ); + }), + }, + actions: { + ...state.points.actions, + processes: + state.points.actions.processes?.filter((process) => { + return !( + process.startPoint === connection1.point || + process.endPoint === connection1.point || + process.startPoint === connection2.point || + process.endPoint === connection2.point + ); + }) || [], + }, + triggers: state.points.triggers, + }, + }; + return updatedArmBot; + } + } + return state; + }); + + const updatedPaths = updatedStates.filter( + (state) => + state.modeluuid === connection1.model || + state.modeluuid === connection2.model + ); + + console.log("updatedPaths: ", updatedPaths); + updateBackend(updatedPaths); + + setSimulationStates(updatedStates); + }; + + const removeConnections = (deletedModelUUIDs: string[]) => { + const updatedStates = simulationStates.map((state) => { + + // Handle Conveyor + if (state.type === "Conveyor") { + const updatedConveyor: SimulationTypes.ConveyorEventsSchema = { + ...state, + points: state.points.map((point) => { + return { + ...point, + connections: { + ...point.connections, + targets: point.connections.targets.filter( + (target) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + }; + }), + }; + return updatedConveyor; + } + + // Handle Vehicle + else if (state.type === "Vehicle") { + const updatedVehicle: SimulationTypes.VehicleEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter( + (target) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + }, + }; + return updatedVehicle; + } + + // Handle StaticMachine + else if (state.type === "StaticMachine") { + const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = + { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter( + (target) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + }, + }; + return updatedStaticMachine; + } + + // Handle ArmBot + else if (state.type === "ArmBot") { + const updatedArmBot: SimulationTypes.ArmBotEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + + targets: state.points.connections.targets.filter( + (target: any) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + actions: { + ...state.points.actions, + processes: (state.points.actions.processes = + state.points.actions.processes?.filter((process) => { + const matchedStates = simulationStates.filter((s) => + deletedModelUUIDs.includes(s.modeluuid) + ); + + if (matchedStates.length > 0) { + if (matchedStates[0]?.type === "StaticMachine") { + const trigPoints = matchedStates[0]?.points; + + return !( + process.triggerId === trigPoints?.triggers?.uuid + ); + } else if (matchedStates[0]?.type === "Conveyor") { + const trigPoints = matchedStates[0]?.points; + + if (Array.isArray(trigPoints)) { + const nonEmptyTriggers = trigPoints.filter( + (point) => + point && point.triggers && point.triggers.length > 0 + ); + + const allTriggerUUIDs = nonEmptyTriggers + .flatMap((point) => point.triggers) + .map((trigger) => trigger.uuid); + + return !allTriggerUUIDs.includes(process.triggerId); + } + } + } + return true; + })), + }, + }, + }; + return updatedArmBot; + } + + return state; + }); + + const filteredStates = updatedStates.filter( + (state) => !deletedModelUUIDs.includes(state.modeluuid) + ); + + updateBackend(filteredStates); + setSimulationStates(filteredStates); + }; + + return ( + + {simulationStates.flatMap((path) => { + if (path.type === "Conveyor") { + return path.points.flatMap((point) => + point.connections.targets.map((target, index) => { + const targetPath = simulationStates.find((p) => p.modeluuid === target.modelUUID); + if (targetPath?.type !== "Conveyor" && targetPath?.type !== "ArmBot") 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 ( + (groupRefs.current[`${point.uuid}-${target.pointUUID}-${index}`] = el!)} + start={fromWorldPosition.toArray()} + end={toWorldPosition.toArray()} + mid={midPoint.toArray()} + color={deleteTool && hoveredLineKey === `${point.uuid}-${target.pointUUID}-${index}` ? "red" : targetPath?.type === "ArmBot" ? "#42a5f5" : "white"} + lineWidth={4} + dashed={deleteTool && hoveredLineKey === `${point.uuid}-${target.pointUUID}-${index}` ? false : true} + dashSize={0.75} + dashScale={20} + onPointerOver={() => setHoveredLineKey(`${point.uuid}-${target.pointUUID}-${index}`)} + onPointerOut={() => setHoveredLineKey(null)} + onClick={() => { + if (deleteTool) { + const connection1 = { + model: path.modeluuid, + point: point.uuid, + }; + const connection2 = { + model: target.modelUUID, + point: target.pointUUID, + }; + + removeConnection(connection1, connection2); + } + }} + userData={target} + /> + ); + } + return null; + }) + ); + } + + if (path.type === "Vehicle") { + return path.points.connections.targets.map((target, index) => { + const fromSphere = pathsGroupRef.current?.getObjectByProperty("uuid", path.points.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 ( + (groupRefs.current[`${path.points.uuid}-${target.pointUUID}-${index}`] = el!)} + start={fromWorldPosition.toArray()} + end={toWorldPosition.toArray()} + mid={midPoint.toArray()} + color={deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}` ? "red" : "orange"} + lineWidth={4} + dashed={deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}` ? false : true} + dashSize={0.75} + dashScale={20} + onPointerOver={() => setHoveredLineKey(`${path.points.uuid}-${target.pointUUID}-${index}`)} + onPointerOut={() => setHoveredLineKey(null)} + onClick={() => { + if (deleteTool) { + const connection1 = { + model: path.modeluuid, + point: path.points.uuid, + }; + const connection2 = { + model: target.modelUUID, + point: target.pointUUID, + }; + + removeConnection(connection1, connection2); + } + }} + userData={target} + /> + ); + } + return null; + }); + } + + if (path.type === "StaticMachine") { + return path.points.connections.targets.map((target, index) => { + const targetPath = simulationStates.find((p) => p.modeluuid === target.modelUUID); + if (targetPath?.type !== "ArmBot") return null; + + const fromSphere = pathsGroupRef.current?.getObjectByProperty("uuid", path.points.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 ( + (groupRefs.current[`${path.points.uuid}-${target.pointUUID}-${index}`] = el!)} + start={fromWorldPosition.toArray()} + end={toWorldPosition.toArray()} + mid={midPoint.toArray()} + color={deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}` ? "red" : "#42a5f5"} + lineWidth={4} + dashed={deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}` ? false : true} + dashSize={0.75} + dashScale={20} + onPointerOver={() => setHoveredLineKey(`${path.points.uuid}-${target.pointUUID}-${index}`)} + onPointerOut={() => setHoveredLineKey(null)} + onClick={() => { + if (deleteTool) { + const connection1 = { + model: path.modeluuid, + point: path.points.uuid, + }; + const connection2 = { + model: target.modelUUID, + point: target.pointUUID, + }; + + removeConnection(connection1, connection2); + } + }} + userData={target} + /> + ); + } + return null; + }); + } + + return []; + })} + + {currentLine && ( + - setHoveredLineKey( - `${point.uuid}-${target.pointUUID}-${index}` - ) - } - onPointerOut={() => setHoveredLineKey(null)} - onClick={() => { - if (deleteTool) { - const connection1 = { - model: path.modeluuid, - point: point.uuid, - }; - const connection2 = { - model: target.modelUUID, - point: target.pointUUID, - }; - - removeConnections(connection1, connection2); - } - }} - userData={target} - /> - ); - } - return null; - }) - ); - } - - if (path.type === "Vehicle") { - return path.points.connections.targets.map((target, index) => { - const fromSphere = pathsGroupRef.current?.getObjectByProperty( - "uuid", - path.points.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 ( - - (groupRefs.current[ - `${path.points.uuid}-${target.pointUUID}-${index}` - ] = el!) - } - start={fromWorldPosition.toArray()} - end={toWorldPosition.toArray()} - mid={midPoint.toArray()} - color={ - deleteTool && - hoveredLineKey === - `${path.points.uuid}-${target.pointUUID}-${index}` - ? "red" - : "orange" - } - lineWidth={4} - dashed={ - deleteTool && - hoveredLineKey === - `${path.points.uuid}-${target.pointUUID}-${index}` - ? false - : true - } - dashSize={0.75} - dashScale={20} - onPointerOver={() => - setHoveredLineKey( - `${path.points.uuid}-${target.pointUUID}-${index}` - ) - } - onPointerOut={() => setHoveredLineKey(null)} - onClick={() => { - if (deleteTool) { - const connection1 = { - model: path.modeluuid, - point: path.points.uuid, - }; - const connection2 = { - model: target.modelUUID, - point: target.pointUUID, - }; - - removeConnections(connection1, connection2); - } - }} - userData={target} /> - ); - } - return null; - }); - } - - if (path.type === "StaticMachine") { - return path.points.connections.targets.map((target, index) => { - const targetPath = simulationStates.find( - (p) => p.modeluuid === target.modelUUID - ); - if (targetPath?.type !== "ArmBot") return null; - - const fromSphere = pathsGroupRef.current?.getObjectByProperty( - "uuid", - path.points.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 ( - - (groupRefs.current[ - `${path.points.uuid}-${target.pointUUID}-${index}` - ] = el!) - } - start={fromWorldPosition.toArray()} - end={toWorldPosition.toArray()} - mid={midPoint.toArray()} - color={ - deleteTool && - hoveredLineKey === - `${path.points.uuid}-${target.pointUUID}-${index}` - ? "red" - : "#42a5f5" - } - lineWidth={4} - dashed={ - deleteTool && - hoveredLineKey === - `${path.points.uuid}-${target.pointUUID}-${index}` - ? false - : true - } - dashSize={0.75} - dashScale={20} - onPointerOver={() => - setHoveredLineKey( - `${path.points.uuid}-${target.pointUUID}-${index}` - ) - } - onPointerOut={() => setHoveredLineKey(null)} - onClick={() => { - if (deleteTool) { - const connection1 = { - model: path.modeluuid, - point: path.points.uuid, - }; - const connection2 = { - model: target.modelUUID, - point: target.pointUUID, - }; - - removeConnections(connection1, connection2); - } - }} - userData={target} - /> - ); - } - return null; - }); - } - - return []; - })} - - {currentLine && ( - - )} - - ); + )} + + ); } export default PathConnector;