feat: Implement zone management features including creation, deletion, and updates

- Added zone handling in the Line component for removing and updating zones based on points.
- Enhanced Point component to support snapping and updating zone positions.
- Introduced zone-related API calls for upserting and deleting zones.
- Updated zone store to manage zones more effectively, including methods for removing zones by points and getting zones by point IDs.
- Improved zone instance rendering to handle dynamic point connections.
- Refactored zone creation logic to allow for better interaction and snapping behavior.
- Updated API endpoints for zone operations to use version 1 of the API.
This commit is contained in:
2025-06-30 12:22:42 +05:30
parent 90df6c2b01
commit 7124a819b6
18 changed files with 601 additions and 697 deletions

View File

@@ -1,651 +0,0 @@
// import React, { useState, useEffect, useMemo, useRef } from "react";
// import { Html, Line, Sphere } from "@react-three/drei";
// import { useThree, useFrame } from "@react-three/fiber";
// import * as THREE from "three";
// import {
// useActiveLayer,
// useSocketStore,
// useToggleView,
// useToolMode,
// useRemovedLayer,
// useZones,
// useZonePoints,
// } from "../../../store/builder/store";
// import { getZonesApi } from "../../../services/factoryBuilder/zones/getZonesApi";
// import * as CONSTANTS from "../../../types/world/worldConstants";
// import * as turf from "@turf/turf";
// import { computeArea } from "../functions/computeArea";
// import { useSelectedZoneStore } from "../../../store/visualization/useZoneStore";
// import { useParams } from "react-router-dom";
// import { getUserData } from "../../../functions/getUserData";
// import { useVersionContext } from "../version/versionContext";
// const ZoneGroup: React.FC = () => {
// const { camera, pointer, gl, raycaster, scene, controls } = useThree();
// const [startPoint, setStartPoint] = useState<THREE.Vector3 | null>(null);
// const [endPoint, setEndPoint] = useState<THREE.Vector3 | null>(null);
// const { zones, setZones } = useZones();
// const { zonePoints, setZonePoints } = useZonePoints();
// const [isDragging, setIsDragging] = useState(false);
// const { selectedZone } = useSelectedZoneStore();
// const [draggedSphere, setDraggedSphere] = useState<THREE.Vector3 | null>(null);
// const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
// const { toggleView } = useToggleView();
// const { removedLayer, setRemovedLayer } = useRemovedLayer();
// const { toolMode } = useToolMode();
// const { activeLayer } = useActiveLayer();
// const { socket } = useSocketStore();
// const { selectedVersionStore } = useVersionContext();
// const { selectedVersion } = selectedVersionStore();
// const { projectId } = useParams();
// const { userId, organization } = getUserData();
// const groupsRef = useRef<any>();
// 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 uOuterColor;
// void main(){
// float alpha = 1.0 - vUv.y;
// gl_FragColor = vec4(uOuterColor, alpha);
// }
// `,
// uniforms: {
// uOuterColor: { value: new THREE.Color(CONSTANTS.zoneConfig.color) },
// },
// transparent: true,
// depthWrite: false,
// }),
// []
// );
// useEffect(() => {
// if (!selectedVersion) return;
// getZonesApi(organization, projectId, selectedVersion?.versionId || '').then((data) => {
// if (data && data.length > 0) {
// const fetchedZones = data.map((zone: any) => ({
// zoneUuid: zone.zoneUuid,
// zoneName: zone.zoneName,
// points: zone.points,
// viewPortCenter: zone.viewPortCenter,
// viewPortposition: zone.viewPortposition,
// layer: zone.layer,
// }));
// setZones(fetchedZones);
// const fetchedPoints = data.flatMap((zone: any) =>
// zone.points.slice(0, 4).map((point: [number, number, number]) => new THREE.Vector3(...point))
// );
// setZonePoints(fetchedPoints);
// } else {
// setZones([]);
// }
// }).catch((err) => {
// console.error(err);
// })
// }, [selectedVersion?.versionId]);
// 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.zoneUuid); });
// setRemovedLayer(null);
// }
// }, [removedLayer]);
// useEffect(() => {
// if (toolMode !== "Zone") {
// setStartPoint(null);
// setEndPoint(null);
// }
// if (!toggleView) {
// setStartPoint(null);
// setEndPoint(null);
// }
// }, [toolMode, toggleView]);
// // eslint-disable-next-line react-hooks/exhaustive-deps
// const addZoneToBackend = async (zone: {
// zoneUuid: string;
// zoneName: string;
// points: [number, number, number][];
// layer: string;
// }) => {
// 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 || zone.points.length < 4) return;
// const position = [target[0], 10, target[2]];
// const input = {
// userId: userId,
// versionId: selectedVersion?.versionId || '',
// projectId,
// organization,
// zoneData: {
// zoneName: zone.zoneName,
// zoneUuid: zone.zoneUuid,
// points: zone.points,
// viewPortCenter: target,
// viewPortposition: position,
// layer: zone.layer,
// },
// };
// socket.emit("v1:zone:set", input);
// };
// // eslint-disable-next-line react-hooks/exhaustive-deps
// const updateZoneToBackend = async (zone: {
// zoneUuid: string;
// zoneName: string;
// points: [number, number, number][];
// layer: string;
// }) => {
// 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 || zone.points.length < 4) return;
// const position = [target[0], 10, target[2]];
// const input = {
// userId: userId,
// versionId: selectedVersion?.versionId || '',
// projectId,
// organization,
// zoneData: {
// zoneName: zone.zoneName,
// zoneUuid: zone.zoneUuid,
// points: zone.points,
// viewPortCenter: target,
// viewPortposition: position,
// layer: zone.layer,
// },
// };
// socket.emit("v1:zone:set", input);
// };
// const deleteZoneFromBackend = async (zoneUuid: string) => {
// const input = {
// userId: userId,
// versionId: selectedVersion?.versionId || '',
// projectId,
// organization,
// zoneUuid: zoneUuid,
// };
// socket.emit("v1:zone:delete", input);
// };
// // eslint-disable-next-line react-hooks/exhaustive-deps
// const handleDeleteZone = (zoneUuid: string) => {
// const updatedZones = zones.filter((zone: any) => zone.zoneUuid !== zoneUuid);
// setZones(updatedZones);
// const zoneIndex = zones.findIndex((zone: any) => zone.zoneUuid === zoneUuid);
// 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(zoneUuid);
// };
// 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 && toolMode === "move") {
// 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 && toolMode === 'Zone') {
// isLeftMouseDown = false;
// if (!startPoint) {
// 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) {
// 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 zoneUuid = THREE.MathUtils.generateUUID();
// const newZone = {
// zoneUuid,
// 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 && toolMode === '2D-Delete') {
// 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 zoneUuid = zones[zoneIndex].zoneUuid;
// handleDeleteZone(zoneUuid);
// 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 (!groupsRef.current) return;
// if (isLeftMouseDown) {
// drag = true;
// }
// raycaster.setFromCamera(pointer, camera);
// 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" || toolMode === '2D-Delete' || toolMode === "move") {
// 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, zonePoints, draggedSphere, activeLayer, raycaster, pointer, controls, plane, setZones, setZonePoints, addZoneToBackend, handleDeleteZone, updateZoneToBackend, selectedVersion?.versionId]);
// 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 (
// <group ref={groupsRef} name="zoneGroup">
// <group name="zones" visible={!toggleView}>
// {zones.map((zone: any) => (
// <group
// key={zone.zoneUuid}
// name={zone.zoneName}
// visible={zone.zoneUuid === selectedZone.zoneUuid}
// >
// {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 (
// <mesh
// key={index}
// position={midpoint}
// rotation={[0, -angle, 0]}
// >
// <planeGeometry args={[planeWidth, planeHeight]} />
// <primitive
// object={zoneMaterial.clone()}
// attach="material"
// />
// </mesh>
// );
// })}
// {!toggleView &&
// (() => {
// const points3D = zone.points || [];
// const coords2D = points3D.map((p: any) => [p[0], p[2]]);
// // Ensure the polygon is closed
// if (
// coords2D.length >= 4 &&
// (coords2D[0][0] !== coords2D[coords2D.length - 1][0] ||
// coords2D[0][1] !== coords2D[coords2D.length - 1][1])
// ) {
// coords2D.push(coords2D[0]);
// }
// if (coords2D.length < 4) return null;
// const polygon = turf.polygon([coords2D]);
// const center2D = turf.center(polygon).geometry.coordinates;
// // Calculate the average Y value
// const sumY = points3D.reduce(
// (sum: number, p: any) => sum + p[1],
// 0
// );
// const avgY = points3D.length > 0 ? sumY / points3D.length : 0;
// const htmlPosition: [number, number, number] = [
// center2D[0],
// avgY + (CONSTANTS.zoneConfig.height || 0) + 1.5,
// center2D[1],
// ];
// return (
// <Html
// // data
// key={zone.zoneUuid}
// position={htmlPosition}
// // class
// className="zone-name-wrapper"
// // others
// center
// >
// <div className="zone-name">{zone.zoneName}</div>
// </Html>
// );
// })()}
// </group>
// ))}
// </group>
// <group name="zoneLines" visible={toggleView}>
// {zones
// .filter((zone: any) => zone.layer === activeLayer)
// .map((zone: any) => (
// <Line
// key={zone.zoneUuid}
// points={zone.points}
// color="#007BFF"
// lineWidth={3}
// onClick={(e) => {
// e.stopPropagation();
// if (toolMode === '2D-Delete') {
// handleDeleteZone(zone.zoneUuid);
// }
// }}
// />
// ))}
// </group>
// <group name="zoneArea" visible={toggleView}>
// {zones.map((zone: any, index: any) => {
// if (!toggleView) return null;
// const points3D = zone.points;
// const coords2D = points3D.map((p: any) => [p[0], p[2]]);
// if (
// coords2D.length < 4 ||
// coords2D[0][0] !== coords2D[coords2D.length - 1][0] ||
// coords2D[0][1] !== coords2D[coords2D.length - 1][1]
// ) {
// coords2D.push(coords2D[0]);
// }
// if (coords2D.length < 4) return null;
// const polygon = turf.polygon([coords2D]);
// const center2D = turf.center(polygon).geometry.coordinates;
// const sumY = points3D.reduce((sum: number, p: any) => sum + p[1], 0);
// const avgY = sumY / points3D.length;
// const area = computeArea(points3D, "zone");
// const formattedArea = `${area.toFixed(2)} m²`;
// const htmlPosition: [number, number, number] = [
// center2D[0],
// avgY + CONSTANTS.zoneConfig.height,
// center2D[1],
// ];
// return (
// <Html
// // data
// key={`${index}-${zone}`}
// position={htmlPosition}
// // class
// wrapperClass="distance-text-wrapper"
// className="distance-text"
// // other
// zIndexRange={[1, 0]}
// prepend
// center
// sprite
// >
// <div
// className={`distance area line-${zone}`}
// key={`${index}-${zone}`}
// >
// {zone.zoneName} ({formattedArea})
// </div>
// </Html>
// );
// })}
// </group>
// <group name="zonePoints" visible={toggleView}>
// {zones
// .filter((zone: any) => zone.layer === activeLayer)
// .flatMap((zone: any) =>
// zone.points.slice(0, 4).map((point: any, pointIndex: number) => (
// <Sphere
// key={`${zone.zoneUuid}-point-${pointIndex}`}
// position={new THREE.Vector3(...point)}
// args={[0.3, 16, 16]}
// name={`point-${zone.zoneUuid}-${pointIndex}`}
// >
// <meshBasicMaterial color="red" />
// </Sphere>
// ))
// )}
// </group>
// <group name="tempGroup" visible={toggleView}>
// {startPoint && endPoint && (
// <Line
// points={[
// [startPoint.x, 0.15, startPoint.z],
// [endPoint.x, 0.15, startPoint.z],
// [endPoint.x, 0.15, endPoint.z],
// [startPoint.x, 0.15, endPoint.z],
// [startPoint.x, 0.15, startPoint.z],
// ]}
// color="#C164FF"
// lineWidth={3}
// />
// )}
// </group>
// </group>
// );
// };
// export default ZoneGroup;