import { useThree } from "@react-three/fiber"; import React, { useEffect, useRef } from "react"; import { useAsset3dWidget, useSocketStore, useWidgetSubOption, } from "../../../store/store"; import useModuleStore from "../../../store/useModuleStore"; import { ThreeState } from "../../../types/world/worldTypes"; import * as THREE from "three"; import Throughput from "../../layout/3D-cards/cards/Throughput"; import ProductionCapacity from "../../layout/3D-cards/cards/ProductionCapacity"; import ReturnOfInvestment from "../../layout/3D-cards/cards/ReturnOfInvestment"; import StateWorking from "../../layout/3D-cards/cards/StateWorking"; import { useSelectedZoneStore } from "../../../store/useZoneStore"; import { generateUniqueId } from "../../../functions/generateUniqueId"; import { adding3dWidgets } from "../../../services/realTimeVisulization/zoneData/add3dWidget"; import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zoneData/get3dWidgetData"; import { use3DWidget } from "../../../store/useDroppedObjectsStore"; import { useEditWidgetOptionsStore, useLeftData, useRightClickSelected, useRightSelected, useTopData, useZoneWidgetStore, } from "../../../store/useZone3DWidgetStore"; import { useWidgetStore } from "../../../store/useWidgetStore"; import EditWidgetOption from "../menu/EditWidgetOption"; type WidgetData = { id: string; type: string; position: [number, number, number]; rotation?: [number, number, number]; tempPosition?: [number, number, number]; }; export default function Dropped3dWidgets() { const { widgetSelect } = useAsset3dWidget(); const { activeModule } = useModuleStore(); const { raycaster, gl, scene, mouse, camera }: ThreeState = useThree(); const { widgetSubOption } = useWidgetSubOption(); const { selectedZone } = useSelectedZoneStore(); const { top, setTop } = useTopData(); const { left, setLeft } = useLeftData(); const { rightSelect, setRightSelect } = useRightSelected(); const { setEditWidgetOptions } = useEditWidgetOptionsStore(); const { zoneWidgetData, setZoneWidgetData, addWidget, updateWidgetPosition, updateWidgetRotation, } = useZoneWidgetStore(); const { setWidgets3D } = use3DWidget(); const { visualizationSocket } = useSocketStore(); const { rightClickSelected, setRightClickSelected } = useRightClickSelected(); const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)); // Floor plane for horizontal move const verticalPlane = useRef(new THREE.Plane(new THREE.Vector3(0, 0, 1), 0)); // Vertical plane for vertical move const planeIntersect = useRef(new THREE.Vector3()); // const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); // const verticalPlane = useRef(new THREE.Plane(new THREE.Vector3(0, 0, 1), 0); // const planeIntersect = useRef(new THREE.Vector3()); const rotationStartRef = useRef<[number, number, number]>([0, 0, 0]); const mouseStartRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 }); useEffect(() => { if (activeModule !== "visualization") return; if (!selectedZone.zoneId) return; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; async function get3dWidgetData() { const result = await get3dWidgetZoneData( selectedZone.zoneId, organization ); setWidgets3D(result); const formattedWidgets = result.map((widget: WidgetData) => ({ id: widget.id, type: widget.type, position: widget.position, rotation: widget.rotation || [0, 0, 0], })); setZoneWidgetData(selectedZone.zoneId, formattedWidgets); } get3dWidgetData(); }, [selectedZone.zoneId, activeModule]); useEffect(() => { if (activeModule !== "visualization") return; if (widgetSubOption === "Floating" || widgetSubOption === "2D") return; if (selectedZone.zoneName === "") return; const canvasElement = gl.domElement; const onDrop = async (event: DragEvent) => { event.preventDefault(); const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; if (!widgetSelect.startsWith("ui")) return; const group1 = scene.getObjectByName("itemsGroup"); if (!group1) return; 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") ); if (intersects.length > 0) { const { x, y, z } = intersects[0].point; const newWidget: WidgetData = { id: generateUniqueId(), type: widgetSelect, position: [x, y, z], rotation: [0, 0, 0], }; const add3dWidget = { organization: organization, widget: newWidget, zoneId: selectedZone.zoneId, }; if (visualizationSocket) { visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget); } addWidget(selectedZone.zoneId, newWidget); } }; canvasElement.addEventListener("drop", onDrop); return () => { canvasElement.removeEventListener("drop", onDrop); }; }, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption]); const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || []; useEffect(() => { if (!rightClickSelected) return; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; if (rightSelect === "Duplicate") { const widgetToDuplicate = activeZoneWidgets.find( (w: WidgetData) => w.id === rightClickSelected ); if (!widgetToDuplicate) return; const newWidget: WidgetData = { id: generateUniqueId(), type: widgetToDuplicate.type, position: [ widgetToDuplicate.position[0] + 0.5, widgetToDuplicate.position[1], widgetToDuplicate.position[2] + 0.5, ], rotation: widgetToDuplicate.rotation || [0, 0, 0], }; const add3dWidget = { organization, widget: newWidget, zoneId: selectedZone.zoneId, }; addWidget(selectedZone.zoneId, newWidget); setRightSelect(null); setRightClickSelected(null); } if (rightSelect === "Delete") { const deleteWidget = { organization, widgetId: rightClickSelected, zoneId: selectedZone.zoneId, }; setZoneWidgetData( selectedZone.zoneId, activeZoneWidgets.filter((w: WidgetData) => w.id !== rightClickSelected) ); setRightClickSelected(null); setRightSelect(null); } }, [rightSelect, rightClickSelected]); useEffect(() => { const handleMouseDown = (event: MouseEvent) => { if (!rightClickSelected || !rightSelect) return; if (rightSelect === "RotateX" || rightSelect === "RotateY") { mouseStartRef.current = { x: event.clientX, y: event.clientY }; const selectedZone = Object.keys(zoneWidgetData).find( (zoneId: string) => zoneWidgetData[zoneId].some( (widget: WidgetData) => widget.id === rightClickSelected ) ); if (!selectedZone) return; const selectedWidget = zoneWidgetData[selectedZone].find( (widget: WidgetData) => widget.id === rightClickSelected ); if (selectedWidget) { rotationStartRef.current = selectedWidget.rotation || [0, 0, 0]; } } }; const handleMouseMove = (event: MouseEvent) => { if (!rightClickSelected || !rightSelect) return; const selectedZone = Object.keys(zoneWidgetData).find((zoneId: string) => zoneWidgetData[zoneId].some( (widget: WidgetData) => widget.id === rightClickSelected ) ); if (!selectedZone) return; const selectedWidget = zoneWidgetData[selectedZone].find( (widget: WidgetData) => widget.id === rightClickSelected ); if (!selectedWidget) return; const rect = gl.domElement.getBoundingClientRect(); mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; raycaster.setFromCamera(mouse, camera); if ( rightSelect === "Horizontal Move" && raycaster.ray.intersectPlane(plane.current, planeIntersect.current) ) { const newPosition: [number, number, number] = [ planeIntersect.current.x, selectedWidget.position[1], planeIntersect.current.z, ]; updateWidgetPosition(selectedZone, rightClickSelected, newPosition); console.log("Horizontal Move - Final Position:", newPosition); } if (rightSelect === "Vertical Move") { if ( raycaster.ray.intersectPlane( verticalPlane.current, planeIntersect.current ) ) { updateWidgetPosition(selectedZone, rightClickSelected, [ selectedWidget.position[0], planeIntersect.current.y, selectedWidget.position[2], ]); } } if (rightSelect === "RotateX") { const deltaX = event.clientX - mouseStartRef.current.x; const rotationSpeed = 0.03; const newRotation: [number, number, number] = [ rotationStartRef.current[0] + deltaX * rotationSpeed, rotationStartRef.current[1], rotationStartRef.current[2], ]; updateWidgetRotation(selectedZone, rightClickSelected, newRotation); } if (rightSelect === "RotateY") { const deltaY = event.clientY - mouseStartRef.current.y; const rotationSpeed = 0.03; const newRotation: [number, number, number] = [ rotationStartRef.current[0], rotationStartRef.current[1] + deltaY * rotationSpeed, rotationStartRef.current[2], ]; updateWidgetRotation(selectedZone, rightClickSelected, newRotation); } if (rightSelect === "RotateZ") { const deltaX = event.movementX; const rotationSpeed = 0.03; const currentRotation = selectedWidget.rotation || [0, 0, 0]; const newRotation: [number, number, number] = [ currentRotation[0], currentRotation[1], currentRotation[2] + deltaX * rotationSpeed, ]; updateWidgetRotation(selectedZone, rightClickSelected, newRotation); } }; const handleMouseUp = () => { if ( rightClickSelected && (rightSelect === "Horizontal Move" || rightSelect === "Vertical Move" || rightSelect === "RotateX" || rightSelect === "RotateY" || rightSelect === "RotateZ") ) { setTimeout(() => { setRightClickSelected(null); setRightSelect(null); }, 50); } }; window.addEventListener("mousedown", handleMouseDown); window.addEventListener("mousemove", handleMouseMove); window.addEventListener("mouseup", handleMouseUp); return () => { window.removeEventListener("mousedown", handleMouseDown); window.removeEventListener("mousemove", handleMouseMove); window.removeEventListener("mouseup", handleMouseUp); }; }, [rightClickSelected, rightSelect, zoneWidgetData, gl]); return ( <> {activeZoneWidgets.map( ({ id, type, position, rotation = [0, 0, 0] }: WidgetData) => { const handleRightClick = (event: React.MouseEvent, id: string) => { event.preventDefault(); const canvasElement = document.getElementById( "real-time-vis-canvas" ); if (!canvasElement) throw new Error("Canvas element not found"); const canvasRect = canvasElement.getBoundingClientRect(); const relativeX = event.clientX - canvasRect.left; const relativeY = event.clientY - canvasRect.top; setEditWidgetOptions(true); setRightClickSelected(id); setTop(relativeY); setLeft(relativeX); }; switch (type) { case "ui-Widget 1": return ( handleRightClick(e, id)} /> ); case "ui-Widget 2": return ( handleRightClick(e, id)} /> ); case "ui-Widget 3": return ( handleRightClick(e, id)} /> ); case "ui-Widget 4": return ( handleRightClick(e, id)} /> ); default: return null; } } )} ); }