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, 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 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 { 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 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 !== 0) return 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) => { isSelecting = true; if (helper.isDown && event.ctrlKey && duplicatedObjects.length === 0 && isCtrlSelecting) { selectionBox.endPoint.set(pointer.x, pointer.y, 0); } }; const onPointerUp = (event: PointerEvent) => { 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(); clearSelection(); } if (!toggleView && activeModule === "builder") { helper.enabled = true; canvasElement.addEventListener("pointerdown", onPointerDown); canvasElement.addEventListener("pointermove", onPointerMove); canvasElement.addEventListener("contextmenu", onContextMenu); canvasElement.addEventListener("pointerup", onPointerUp); 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 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('v1:FloorItems: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(); } } }); itemsGroupRef.current?.remove(selectedMesh); }); const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid)); setFloorItems(updatedItems); } toast.success("Selected models removed!"); clearSelection(); }; return ( <> ); }; export default SelectionControls;