import React, { useState, useEffect, useMemo, useRef } from "react"; import { Line, Sphere } from "@react-three/drei"; import { useThree, useFrame } from "@react-three/fiber"; import * as THREE from "three"; import { useActiveLayer, useDeleteModels, useDeletePointOrLine, useMovePoint, useSocketStore, useToggleView, useToolMode, useRemovedLayer, useZones, useZonePoints } from "../../../store/store"; // import { setZonesApi } from "../../../services/factoryBuilder/zones/setZonesApi"; // import { deleteZonesApi } from "../../../services/factoryBuilder/zones/deleteZoneApi"; import { getZonesApi } from "../../../services/factoryBuilder/zones/getZonesApi"; import * as CONSTANTS from '../../../types/world/worldConstants'; const ZoneGroup: React.FC = () => { const { camera, pointer, gl, raycaster, scene, controls } = useThree(); const [startPoint, setStartPoint] = useState(null); const [endPoint, setEndPoint] = useState(null); const { zones, setZones } = useZones(); const { zonePoints, setZonePoints } = useZonePoints(); const [isDragging, setIsDragging] = useState(false); const [draggedSphere, setDraggedSphere] = useState(null); const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); const { toggleView } = useToggleView(); const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine(); const { removedLayer, setRemovedLayer } = useRemovedLayer(); const { toolMode, setToolMode } = useToolMode(); const { movePoint, setMovePoint } = useMovePoint(); const { deleteModels, setDeleteModels } = useDeleteModels(); const { activeLayer, setActiveLayer } = useActiveLayer(); const { socket } = useSocketStore(); const groupsRef = useRef(); const zoneMaterial = useMemo(() => new THREE.ShaderMaterial({ side: THREE.DoubleSide, vertexShader: ` varying vec2 vUv; void main(){ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); vUv = uv; } `, fragmentShader: ` varying vec2 vUv; uniform vec3 uColor; void main(){ float alpha = 1.0 - vUv.y; gl_FragColor = vec4(uColor, alpha); } `, uniforms: { uColor: { value: new THREE.Color(CONSTANTS.zoneConfig.color) }, }, transparent: true, }), []); useEffect(() => { const fetchZones = async () => { const email = localStorage.getItem('email'); if (!email) return; const organization = email.split("@")[1].split(".")[0]; const data = await getZonesApi(organization); if (data.data && data.data.length > 0) { const fetchedZones = data.data.map((zone: any) => ({ zoneId: zone.zoneId, zoneName: zone.zoneName, points: zone.points, viewPortCenter: zone.viewPortCenter, viewPortposition: zone.viewPortposition, layer: zone.layer })); setZones(fetchedZones); const fetchedPoints = data.data.flatMap((zone: any) => zone.points.slice(0, 4).map((point: [number, number, number]) => new THREE.Vector3(...point)) ); setZonePoints(fetchedPoints); } }; fetchZones(); }, []); useEffect(() => { localStorage.setItem('zones', JSON.stringify(zones)); }, [zones]) useEffect(() => { if (removedLayer) { const updatedZones = zones.filter((zone: any) => zone.layer !== removedLayer); setZones(updatedZones); const updatedzonePoints = zonePoints.filter((_: any, index: any) => { const zoneIndex = Math.floor(index / 4); return zones[zoneIndex]?.layer !== removedLayer; }); setZonePoints(updatedzonePoints); zones.filter((zone: any) => zone.layer === removedLayer).forEach((zone: any) => { deleteZoneFromBackend(zone.zoneId); }); setRemovedLayer(null); } }, [removedLayer]); useEffect(() => { if (toolMode !== "Zone") { setStartPoint(null); setEndPoint(null); } else { setDeletePointOrLine(false); setMovePoint(false); setDeleteModels(false); } if (!toggleView) { setStartPoint(null); setEndPoint(null); } }, [toolMode, toggleView]); const addZoneToBackend = async (zone: { zoneId: string; zoneName: string; points: [number, number, number][]; layer: string }) => { const email = localStorage.getItem('email'); const userId = localStorage.getItem('userId'); const organization = (email!.split("@")[1]).split(".")[0]; const calculateCenter = (points: number[][]) => { if (!points || points.length === 0) return null; let sumX = 0, sumY = 0, sumZ = 0; const numPoints = points.length; points.forEach(([x, y, z]) => { sumX += x; sumY += y; sumZ += z; }); return [sumX / numPoints, sumY / numPoints, sumZ / numPoints] as [number, number, number]; }; const target: [number, number, number] | null = calculateCenter(zone.points); if (!target) return; const position = [target[0], 10, target[2]]; const input = { userId: userId, organization: organization, zoneData: { zoneName: zone.zoneName, zoneId: zone.zoneId, points: zone.points, viewPortCenter: target, viewPortposition: position, layer: zone.layer } } socket.emit('v2:zone:set', input); }; const updateZoneToBackend = async (zone: { zoneId: string; zoneName: string; points: [number, number, number][]; layer: string }) => { const email = localStorage.getItem('email'); const userId = localStorage.getItem('userId'); const organization = (email!.split("@")[1]).split(".")[0]; const calculateCenter = (points: number[][]) => { if (!points || points.length === 0) return null; let sumX = 0, sumY = 0, sumZ = 0; const numPoints = points.length; points.forEach(([x, y, z]) => { sumX += x; sumY += y; sumZ += z; }); return [sumX / numPoints, sumY / numPoints, sumZ / numPoints] as [number, number, number]; }; const target: [number, number, number] | null = calculateCenter(zone.points); if (!target) return; const position = [target[0], 10, target[2]]; const input = { userId: userId, organization: organization, zoneData: { zoneName: zone.zoneName, zoneId: zone.zoneId, points: zone.points, viewPortCenter: target, viewPortposition: position, layer: zone.layer } } socket.emit('v2:zone:set', input); }; const deleteZoneFromBackend = async (zoneId: string) => { const email = localStorage.getItem('email'); const userId = localStorage.getItem('userId'); const organization = (email!.split("@")[1]).split(".")[0]; const input = { userId: userId, organization: organization, zoneId: zoneId } socket.emit('v2:zone:delete', input); }; const handleDeleteZone = (zoneId: string) => { const updatedZones = zones.filter((zone: any) => zone.zoneId !== zoneId); setZones(updatedZones); const zoneIndex = zones.findIndex((zone: any) => zone.zoneId === zoneId); if (zoneIndex !== -1) { const zonePointsToRemove = zonePoints.slice(zoneIndex * 4, zoneIndex * 4 + 4); zonePointsToRemove.forEach((point: any) => groupsRef.current.remove(point)); const updatedzonePoints = zonePoints.filter((_: any, index: any) => index < zoneIndex * 4 || index >= zoneIndex * 4 + 4); setZonePoints(updatedzonePoints); } deleteZoneFromBackend(zoneId); }; useEffect(() => { if (!camera || !toggleView) return; const canvasElement = gl.domElement; let drag = false; let isLeftMouseDown = false; const onMouseDown = (evt: any) => { if (evt.button === 0) { isLeftMouseDown = true; drag = false; raycaster.setFromCamera(pointer, camera); const intersects = raycaster.intersectObjects(groupsRef.current.children, true); if (intersects.length > 0 && movePoint) { const clickedObject = intersects[0].object; const sphereIndex = zonePoints.findIndex((point: any) => point.equals(clickedObject.position)); if (sphereIndex !== -1) { (controls as any).enabled = false; setDraggedSphere(zonePoints[sphereIndex]); setIsDragging(true); } } } }; const onMouseUp = (evt: any) => { if (evt.button === 0 && !drag && !isDragging && !deletePointOrLine) { isLeftMouseDown = false; if (!startPoint && !movePoint) { raycaster.setFromCamera(pointer, camera); const intersectionPoint = new THREE.Vector3(); const point = raycaster.ray.intersectPlane(plane, intersectionPoint); if (point) { setStartPoint(point); setEndPoint(null); } } else if (startPoint && !movePoint) { raycaster.setFromCamera(pointer, camera); const intersectionPoint = new THREE.Vector3(); const point = raycaster.ray.intersectPlane(plane, intersectionPoint); if (!point) return; const points = [ [startPoint.x, 0.15, startPoint.z], [point.x, 0.15, startPoint.z], [point.x, 0.15, point.z], [startPoint.x, 0.15, point.z], [startPoint.x, 0.15, startPoint.z], ] as [number, number, number][]; const zoneName = `Zone ${zones.length + 1}`; const zoneId = THREE.MathUtils.generateUUID(); const newZone = { zoneId, zoneName, points: points, layer: activeLayer }; const newZones = [...zones, newZone]; setZones(newZones); const newzonePoints = [ new THREE.Vector3(startPoint.x, 0.15, startPoint.z), new THREE.Vector3(point.x, 0.15, startPoint.z), new THREE.Vector3(point.x, 0.15, point.z), new THREE.Vector3(startPoint.x, 0.15, point.z), ]; const updatedZonePoints = [...zonePoints, ...newzonePoints]; setZonePoints(updatedZonePoints); addZoneToBackend(newZone); setStartPoint(null); setEndPoint(null); } } else if (evt.button === 0 && !drag && !isDragging && deletePointOrLine) { raycaster.setFromCamera(pointer, camera); const intersects = raycaster.intersectObjects(groupsRef.current.children, true); if (intersects.length > 0) { const clickedObject = intersects[0].object; const sphereIndex = zonePoints.findIndex((point: any) => point.equals(clickedObject.position)); if (sphereIndex !== -1) { const zoneIndex = Math.floor(sphereIndex / 4); const zoneId = zones[zoneIndex].zoneId; handleDeleteZone(zoneId); return; } } } if (evt.button === 0) { if (isDragging && draggedSphere) { setIsDragging(false); setDraggedSphere(null); const sphereIndex = zonePoints.findIndex((point: any) => point === draggedSphere); if (sphereIndex !== -1) { const zoneIndex = Math.floor(sphereIndex / 4); if (zoneIndex !== -1 && zones[zoneIndex]) { updateZoneToBackend(zones[zoneIndex]); } } } } }; const onMouseMove = () => { if (isLeftMouseDown) { drag = true; } raycaster.setFromCamera(pointer, camera); const intersects = raycaster.intersectObjects(groupsRef.current.children, true); if (intersects.length > 0 && intersects[0].object.name.includes('point')) { gl.domElement.style.cursor = movePoint ? "pointer" : "default"; } else { gl.domElement.style.cursor = "default"; } if (isDragging && draggedSphere) { raycaster.setFromCamera(pointer, camera); const intersectionPoint = new THREE.Vector3(); const point = raycaster.ray.intersectPlane(plane, intersectionPoint); if (point) { draggedSphere.set(point.x, 0.15, point.z); const sphereIndex = zonePoints.findIndex((point: any) => point === draggedSphere); if (sphereIndex !== -1) { const zoneIndex = Math.floor(sphereIndex / 4); const cornerIndex = sphereIndex % 4; const updatedZones = zones.map((zone: any, index: number) => { if (index === zoneIndex) { const updatedPoints = [...zone.points]; updatedPoints[cornerIndex] = [point.x, 0.15, point.z]; updatedPoints[4] = updatedPoints[0]; return { ...zone, points: updatedPoints }; } return zone; }); setZones(updatedZones); } } } }; const onContext = (event: any) => { event.preventDefault(); setStartPoint(null); setEndPoint(null); }; if (toolMode === 'Zone' || deletePointOrLine || movePoint) { canvasElement.addEventListener("mousedown", onMouseDown); canvasElement.addEventListener("mouseup", onMouseUp); canvasElement.addEventListener("mousemove", onMouseMove); canvasElement.addEventListener("contextmenu", onContext); } return () => { canvasElement.removeEventListener("mousedown", onMouseDown); canvasElement.removeEventListener("mouseup", onMouseUp); canvasElement.removeEventListener("mousemove", onMouseMove); canvasElement.removeEventListener("contextmenu", onContext); }; }, [gl, camera, startPoint, toggleView, scene, toolMode, zones, isDragging, deletePointOrLine, zonePoints, draggedSphere, movePoint, activeLayer]); useFrame(() => { if (!startPoint) return; raycaster.setFromCamera(pointer, camera); const intersectionPoint = new THREE.Vector3(); const point = raycaster.ray.intersectPlane(plane, intersectionPoint); if (point) { setEndPoint(point); } }); return ( {zones .map((zone: any) => ( {zone.points.slice(0, -1).map((point: [number, number, number], index: number) => { const nextPoint = zone.points[index + 1]; const point1 = new THREE.Vector3(point[0], point[1], point[2]); const point2 = new THREE.Vector3(nextPoint[0], nextPoint[1], nextPoint[2]); const planeWidth = point1.distanceTo(point2); const planeHeight = CONSTANTS.zoneConfig.height; const midpoint = new THREE.Vector3((point1.x + point2.x) / 2, (CONSTANTS.zoneConfig.height / 2) + ((zone.layer - 1) * CONSTANTS.zoneConfig.height), (point1.z + point2.z) / 2); const angle = Math.atan2(point2.z - point1.z, point2.x - point1.x); return ( ); })} ))} {zones .filter((zone: any) => zone.layer === activeLayer) .map((zone: any) => ( { e.stopPropagation(); if (deletePointOrLine) { handleDeleteZone(zone.zoneId); } }} /> ))} {zones.filter((zone: any) => zone.layer === activeLayer).flatMap((zone: any) => ( zone.points.slice(0, 4).map((point: any, pointIndex: number) => ( )) ))} {startPoint && endPoint && ( )} ); }; export default ZoneGroup;