import * as THREE from "three"; import { useCallback, 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 { useSelectedAssets, useSocketStore, useToggleView, } from "../../../../store/builder/store"; import BoundingBox from "./boundingBoxHelper"; // 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"; import { useEventsStore } from "../../../../store/simulation/useEventsStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; import { useParams } from "react-router-dom"; import { useAssetsStore } from "../../../../store/builder/useAssetStore"; const SelectionControls: React.FC = () => { const { camera, controls, gl, scene, raycaster, pointer } = useThree(); 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 { activeModule } = useModuleStore(); const { socket } = useSocketStore(); const { removeAsset } = useAssetsStore(); const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]); const { projectId } = useParams(); const isDragging = useRef(false); const isLeftMouseDown = useRef(false); const isSelecting = useRef(false); const isRightClick = useRef(false); const rightClickMoved = useRef(false); const isCtrlSelecting = useRef(false); const isShiftSelecting = useRef(false); useEffect(() => { if (!camera || !scene || toggleView) return; const canvasElement = gl.domElement; canvasElement.tabIndex = 0; const helper = new SelectionHelper(gl); const onPointerDown = (event: PointerEvent) => { if (event.button === 2) { isRightClick.current = true; rightClickMoved.current = false; } else if (event.button === 0) { isSelecting.current = false; isCtrlSelecting.current = event.ctrlKey; isShiftSelecting.current = event.shiftKey; isLeftMouseDown.current = true; isDragging.current = false; 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.current) { rightClickMoved.current = true; } if (isLeftMouseDown.current) { isDragging.current = true; } isSelecting.current = true; if (helper.isDown && event.ctrlKey && duplicatedObjects.length === 0 && isCtrlSelecting.current) { selectionBox.endPoint.set(pointer.x, pointer.y, 0); } }; const onPointerUp = (event: PointerEvent) => { if (event.button === 2 && !event.ctrlKey && !event.shiftKey) { isRightClick.current = false; rightClickMoved.current = false; if (!rightClickMoved.current) { clearSelection(); } return; } if (isSelecting.current && isCtrlSelecting.current) { isCtrlSelecting.current = false; isSelecting.current = false; if (event.ctrlKey && duplicatedObjects.length === 0) { selectAssets(); } } else if (!isSelecting.current && selectedAssets.length > 0 && ((!event.ctrlKey && !event.shiftKey && pastedObjects.length === 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) || event.button !== 0)) { clearSelection(); helper.enabled = true; isCtrlSelecting.current = false; } else if (controls) { (controls as any).enabled = true; } if (!isDragging.current && isLeftMouseDown.current && isShiftSelecting.current && event.shiftKey) { isShiftSelecting.current = false; isLeftMouseDown.current = false; isDragging.current = false; raycaster.setFromCamera(pointer, camera); const intersects = raycaster.intersectObjects(scene.children, true) .filter( (intersect) => !intersect.object.name.includes("Roof") && !intersect.object.name.includes("MeasurementReference") && !intersect.object.name.includes("agv-collider") && !intersect.object.name.includes("zonePlane") && !intersect.object.name.includes("SelectionGroup") && !intersect.object.name.includes("selectionAssetGroup") && !intersect.object.name.includes("SelectionGroupBoundingBoxLine") && !intersect.object.name.includes("SelectionGroupBoundingBox") && !intersect.object.name.includes("SelectionGroupBoundingLine") && intersect.object.type !== "GridHelper" ); if (intersects.length > 0) { const intersect = intersects[0]; const intersectObject = intersect.object; let currentObject: THREE.Object3D | null = intersectObject; while (currentObject) { if (currentObject.userData.modelUuid) { break; } currentObject = currentObject.parent || null; } if (currentObject) { const updatedSelections = new Set(selectedAssets); if (updatedSelections.has(currentObject)) { updatedSelections.delete(currentObject); } else { updatedSelections.add(currentObject); } const selected = Array.from(updatedSelections); setSelectedAssets(selected); } } } }; 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.current) { 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, rotatedObjects, activeModule,]); useEffect(() => { if (activeModule !== "builder") { clearSelection(); } }, [activeModule]); useFrame(() => { console.log(rightClickMoved.current); if (pastedObjects.length === 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { selectionGroup.current.position.set(0, 0, 0); } }); const selectAssets = useCallback(() => { selectionBox.endPoint.set(pointer.x, pointer.y, 0); if (controls) (controls as any).enabled = true; let selectedObjects = selectionBox.select(); let Objects = new Set(); selectedObjects.forEach((object) => { let currentObject: THREE.Object3D | null = object; while (currentObject) { if (currentObject.userData.modelUuid) { 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); }, [selectionBox, pointer, controls, selectedAssets, setSelectedAssets]); 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 userId = localStorage.getItem("userId"); 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.userData.modelUuid, selectedMesh.userData.modelName); //SOCKET const data = { organization: organization, modelUuid: selectedMesh.userData.modelUuid, modelName: selectedMesh.userData.modelName, socketId: socket.id, projectId, userId }; const response = socket.emit("v1:model-asset:delete", data); useEventsStore.getState().removeEvent(selectedMesh.uuid); useProductStore.getState().deleteEvent(selectedMesh.uuid); if (response) { removeAsset(selectedMesh.uuid); echo.success("Model Removed!"); } 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(); } } }); }); selectedUUIDs.forEach((uuid: string) => { removeAsset(uuid); }); } echo.success("Selected models removed!"); clearSelection(); }; return ( <> ); }; export default SelectionControls;