import * as THREE from "three"; 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 BoundingBox from "./boundingBoxHelper"; import { toast } from "react-toastify"; // import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; import * as SimulationTypes from "../../../../types/simulation"; import DuplicationControls from "./duplicationControls"; import CopyPasteControls from "./copyPasteControls"; import MoveControls from "./moveControls"; 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] ); useEffect(() => { if (!camera || !scene || toggleView) return; const canvasElement = gl.domElement; canvasElement.tabIndex = 0; const itemsGroup: any = scene.getObjectByName("itemsGroup"); itemsGroupRef.current = itemsGroup; let isSelecting = false; let isRightClick = false; let rightClickMoved = false; let isCtrlSelecting = false; 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); } } }; 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 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) ); setFloorItems(updatedItems); } toast.success("Selected models removed!"); clearSelection(); }; return ( <> ); }; export default SelectionControls;