diff --git a/app/src/modules/builder/asset/models/model/model.tsx b/app/src/modules/builder/asset/models/model/model.tsx index 9354232..2ffd49d 100644 --- a/app/src/modules/builder/asset/models/model/model.tsx +++ b/app/src/modules/builder/asset/models/model/model.tsx @@ -4,7 +4,7 @@ import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils'; import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; import { ThreeEvent, useFrame, useThree } from '@react-three/fiber'; -import { useActiveTool, useDeletableFloorItem, useRenderDistance, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store'; +import { useActiveTool, useDeletableFloorItem, useLimitDistance, useRenderDistance, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store'; import { AssetBoundingBox } from '../../functions/assetBoundingBox'; import { CameraControls } from '@react-three/drei'; import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore'; @@ -34,6 +34,7 @@ function Model({ asset }: { readonly asset: Asset }) { const { socket } = useSocketStore(); const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem(); const { setSelectedFloorItem } = useSelectedFloorItem(); + const { limitDistance } = useLimitDistance(); const { renderDistance } = useRenderDistance(); const [isRendered, setIsRendered] = useState(false); const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; @@ -125,10 +126,16 @@ function Model({ asset }: { readonly asset: Asset }) { useFrame(() => { const assetPosition = new THREE.Vector3(...asset.position); - if (!isRendered && assetPosition.distanceTo(camera.position) <= renderDistance) { - setIsRendered(true); - } else if (isRendered && assetPosition.distanceTo(camera.position) > renderDistance) { - setIsRendered(false); + if (limitDistance) { + if (!isRendered && assetPosition.distanceTo(camera.position) <= renderDistance) { + setIsRendered(true); + } else if (isRendered && assetPosition.distanceTo(camera.position) > renderDistance) { + setIsRendered(false); + } + } else { + if (!isRendered) { + setIsRendered(true); + } } }) diff --git a/app/src/modules/builder/floor/floorCreator/floorCreator.tsx b/app/src/modules/builder/floor/floorCreator/floorCreator.tsx index 35ba59e..acab425 100644 --- a/app/src/modules/builder/floor/floorCreator/floorCreator.tsx +++ b/app/src/modules/builder/floor/floorCreator/floorCreator.tsx @@ -30,7 +30,7 @@ function FloorCreator() { const [tempPoints, setTempPoints] = useState([]); const [isCreating, setIsCreating] = useState(false); - const { floorDepth, isBeveled, bevelStrength, sideMaterial, topMaterial, snappedPosition, snappedPoint } = useBuilderStore(); + const { floorDepth, isBeveled, bevelStrength, sideMaterial, topMaterial, snappedPosition, snappedPoint, setSnappedPoint, setSnappedPosition } = useBuilderStore(); useEffect(() => { const canvasElement = gl.domElement; @@ -64,11 +64,11 @@ function FloorCreator() { const pointIntersects = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Floor-Point'); - const floorIntersect = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Floor-Line'); + // const floorIntersect = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Floor-Line'); - if (floorIntersect && !pointIntersects) { + // if (floorIntersect && !pointIntersects) { - } + // } const newPoint: Point = { pointUuid: THREE.MathUtils.generateUUID(), @@ -89,7 +89,45 @@ function FloorCreator() { newPoint.position = snappedPosition; } - if (pointIntersects) { + if (tempPoints.length > 2 && isCreating && snappedPoint && snappedPoint.pointUuid === tempPoints[0].pointUuid) { + const floor: Floor = { + floorUuid: THREE.MathUtils.generateUUID(), + floorName: "Floor", + points: tempPoints, + topMaterial, + sideMaterial, + floorDepth, + isBeveled, + bevelStrength, + decals: [], + }; + + addFloor(floor); + if (projectId) { + + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', floor); + + // SOCKET + + const data = { + floorData: floor, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + + } + setTempPoints([]); + setIsCreating(false); + } else if (isCreating && snappedPoint && !tempPoints.some(p => p.pointUuid === snappedPoint.pointUuid)) { + setTempPoints(prev => [...prev, newPoint]); + setIsCreating(true); + } else if (pointIntersects) { if (tempPoints.length > 2 && isCreating && pointIntersects.object.uuid === tempPoints[0].pointUuid) { const floor: Floor = { floorUuid: THREE.MathUtils.generateUUID(), @@ -125,10 +163,7 @@ function FloorCreator() { } setTempPoints([]); setIsCreating(false); - } else if (tempPoints.length === 0 || (tempPoints.length > 1 && pointIntersects.object.uuid !== tempPoints[tempPoints.length - 2].pointUuid)) { - tempPoints.push(pointIntersects.object.userData as Point); - setIsCreating(true); - } else { + } else if (tempPoints.length === 0 || (tempPoints.length > 1 && !tempPoints.slice(1).some(p => p.pointUuid === pointIntersects.object.uuid))) { tempPoints.push(pointIntersects.object.userData as Point); setIsCreating(true); } @@ -182,6 +217,10 @@ function FloorCreator() { }; if (toolMode === "Floor" && toggleView) { + if (tempPoints.length === 0) { + setSnappedPosition(null); + setSnappedPoint(null); + } canvasElement.addEventListener("mousedown", onMouseDown); canvasElement.addEventListener("mouseup", onMouseUp); canvasElement.addEventListener("mousemove", onMouseMove); diff --git a/app/src/modules/builder/floor/floorCreator/referenceFloor.tsx b/app/src/modules/builder/floor/floorCreator/referenceFloor.tsx index dde2002..f663df9 100644 --- a/app/src/modules/builder/floor/floorCreator/referenceFloor.tsx +++ b/app/src/modules/builder/floor/floorCreator/referenceFloor.tsx @@ -36,7 +36,7 @@ function ReferenceFloor({ tempPoints }: Readonly) { setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]); if (!intersectionPoint) return; - const snapped = snapFloorPoint([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z], tempPoints.slice(0, -2)); + const snapped = snapFloorPoint([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z], [tempPoints[0]]); if (snapped.isSnapped && snapped.snappedPoint) { finalPosition.current = snapped.position; diff --git a/app/src/modules/builder/groups/zoneGroup.tsx b/app/src/modules/builder/groups/zoneGroup.tsx deleted file mode 100644 index 3339c20..0000000 --- a/app/src/modules/builder/groups/zoneGroup.tsx +++ /dev/null @@ -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(null); -// const [endPoint, setEndPoint] = useState(null); -// const { zones, setZones } = useZones(); -// const { zonePoints, setZonePoints } = useZonePoints(); -// const [isDragging, setIsDragging] = useState(false); -// const { selectedZone } = useSelectedZoneStore(); -// const [draggedSphere, setDraggedSphere] = useState(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(); - -// 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 ( -// -// -// {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 ( -// -// -// -// -// ); -// })} -// {!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 ( -// -//
{zone.zoneName}
-// -// ); -// })()} -//
-// ))} -//
-// -// {zones -// .filter((zone: any) => zone.layer === activeLayer) -// .map((zone: any) => ( -// { -// e.stopPropagation(); -// if (toolMode === '2D-Delete') { -// handleDeleteZone(zone.zoneUuid); -// } -// }} -// /> -// ))} -// -// -// {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 ( -// -//
-// {zone.zoneName} ({formattedArea}) -//
-// -// ); -// })} -//
- -// -// {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; diff --git a/app/src/modules/builder/line/line.tsx b/app/src/modules/builder/line/line.tsx index 5cf48bc..cc53b9d 100644 --- a/app/src/modules/builder/line/line.tsx +++ b/app/src/modules/builder/line/line.tsx @@ -14,6 +14,8 @@ import { getUserData } from '../../../functions/getUserData'; // import { deleteWallApi } from '../../../services/factoryBuilder/wall/deleteWallApi'; // import { upsertFloorApi } from '../../../services/factoryBuilder/floor/upsertFloorApi'; // import { deleteFloorApi } from '../../../services/factoryBuilder/floor/deleteFloorApi'; +// import { deleteZoneApi } from '../../../services/factoryBuilder/zone/deleteZoneApi'; +// import { upsertZoneApi } from '../../../services/factoryBuilder/zone/upsertZoneApi'; interface LineProps { points: [Point, Point]; @@ -26,9 +28,10 @@ function Line({ points }: Readonly) { const [isDeletable, setIsDeletable] = useState(false); const { socket } = useSocketStore(); const { toolMode } = useToolMode(); - const { wallStore, floorStore } = useSceneContext(); + const { wallStore, floorStore, zoneStore } = useSceneContext(); const { removeWallByPoints, setPosition: setWallPosition, getWallsByPointId } = wallStore(); const { removeFloorByPoints, setPosition: setFloorPosition, getFloorsByPointId } = floorStore(); + const { removeZoneByPoints, setPosition: setZonePosition, getZonesByPointId } = zoneStore(); const { userId, organization } = getUserData(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); @@ -163,6 +166,53 @@ function Line({ points }: Readonly) { setHoveredLine(null); } + if (points[0].pointType === 'Zone' && points[1].pointType === 'Zone') { + const { removedZones, updatedZones } = removeZoneByPoints(points); + if (removedZones.length > 0) { + removedZones.forEach(zone => { + if (projectId) { + + // API + + // deleteZoneApi(projectId, selectedVersion?.versionId || '', zone.zoneUuid); + + // SOCKET + + const data = { + zoneUuid: zone.zoneUuid, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:delete', data); + } + }); + } + if (updatedZones.length > 0) { + updatedZones.forEach(zone => { + if (projectId) { + + // API + + // upsertZoneApi(projectId, selectedVersion?.versionId || '', zone); + + // SOCKET + + const data = { + zoneData: zone, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:add', data); + } + }); + } + } gl.domElement.style.cursor = 'default'; } } @@ -194,6 +244,10 @@ function Line({ points }: Readonly) { setFloorPosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]); setFloorPosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]); } + if (points[0].pointType === 'Zone' && points[1].pointType === 'Zone') { + setZonePosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]); + setZonePosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]); + } } } }; @@ -271,6 +325,33 @@ function Line({ points }: Readonly) { socket.emit('v1:model-Floor:add', data); }) } + } else if (points[0].pointType === 'Zone' && points[1].pointType === 'Zone') { + const updatedZones1 = getZonesByPointId(points[0].pointUuid); + const updatedZones2 = getZonesByPointId(points[1].pointUuid); + const updatedZones = [...updatedZones1, ...updatedZones2].filter((zone, index, self) => index === self.findIndex((z) => z.zoneUuid === zone.zoneUuid)); + + if (updatedZones.length > 0 && projectId) { + updatedZones.forEach(updatedZone => { + + // API + + // upsertZoneApi(projectId, selectedVersion?.versionId || '', updatedZone).catch((error) => { + // console.error('Error updating zone:', error); + // }); + + // SOCKET + + const data = { + zoneData: updatedZone, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:add', data); + }) + } } } diff --git a/app/src/modules/builder/point/helpers/usePointSnapping.tsx b/app/src/modules/builder/point/helpers/usePointSnapping.tsx index 69f5e72..648e4c5 100644 --- a/app/src/modules/builder/point/helpers/usePointSnapping.tsx +++ b/app/src/modules/builder/point/helpers/usePointSnapping.tsx @@ -205,9 +205,9 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string const snapFloorPoint = useCallback((position: [number, number, number], tempPoints?: Point[] | []) => { if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null }; - const otherPoints = getAllOtherFloorPoints(); + let otherPoints = getAllOtherFloorPoints(); if (tempPoints) { - otherPoints.concat(tempPoints); + otherPoints = [...otherPoints, ...tempPoints]; } const currentVec = new THREE.Vector3(...position); @@ -292,9 +292,9 @@ export const usePointSnapping = (currentPoint: { uuid: string, pointType: string const snapZonePoint = useCallback((position: [number, number, number], tempPoints?: Point[] | []) => { if (!currentPoint || !CAN_POINT_SNAP) return { position: position, isSnapped: false, snappedPoint: null }; - const otherPoints = getAllOtherZonePoints(); + let otherPoints = getAllOtherZonePoints(); if (tempPoints) { - otherPoints.concat(tempPoints); + otherPoints = [...otherPoints, ...tempPoints]; } const currentVec = new THREE.Vector3(...position); diff --git a/app/src/modules/builder/point/point.tsx b/app/src/modules/builder/point/point.tsx index fe2dd6e..8689ac5 100644 --- a/app/src/modules/builder/point/point.tsx +++ b/app/src/modules/builder/point/point.tsx @@ -14,10 +14,12 @@ import { upsertAisleApi } from '../../../services/factoryBuilder/aisle/upsertAis import { deleteAisleApi } from '../../../services/factoryBuilder/aisle/deleteAisleApi'; // import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi'; // import { deleteWallApi } from '../../../services/factoryBuilder/wall/deleteWallApi'; -import { upsertFloorApi } from '../../../services/factoryBuilder/floor/upsertFloorApi'; +// import { upsertFloorApi } from '../../../services/factoryBuilder/floor/upsertFloorApi'; +// import { deleteFloorApi } from '../../../services/factoryBuilder/floor/deleteFloorApi'; +// import { upsertZoneApi } from '../../../services/factoryBuilder/zone/upsertZoneApi'; +// import { deleteZoneApi } from '../../../services/factoryBuilder/zone/deleteZoneApi'; import { getUserData } from '../../../functions/getUserData'; -import { deleteFloorApi } from '../../../services/factoryBuilder/floor/deleteFloorApi'; function Point({ point }: { readonly point: Point }) { const materialRef = useRef(null); @@ -27,11 +29,12 @@ function Point({ point }: { readonly point: Point }) { const [dragOffset, setDragOffset] = useState(null); const { socket } = useSocketStore(); const { toolMode } = useToolMode(); - const { aisleStore, wallStore, floorStore } = useSceneContext(); + const { aisleStore, wallStore, floorStore, zoneStore } = useSceneContext(); const { setPosition: setAislePosition, removePoint: removeAislePoint, getAislesByPointId } = aisleStore(); const { setPosition: setWallPosition, removePoint: removeWallPoint, getWallsByPointId } = wallStore(); const { setPosition: setFloorPosition, removePoint: removeFloorPoint, getFloorsByPointId } = floorStore(); - const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle, snapFloorPoint, snapFloorAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position }); + const { setPosition: setZonePosition, removePoint: removeZonePoint, getZonesByPointId } = zoneStore(); + const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle, snapFloorPoint, snapFloorAngle, snapZonePoint, snapZoneAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position }); const { hoveredPoint, setHoveredPoint } = useBuilderStore(); const { userId, organization } = getUserData(); const { selectedVersionStore } = useVersionContext(); @@ -127,6 +130,10 @@ function Point({ point }: { readonly point: Point }) { const floorSnapped = snapFloorAngle(newPosition); const finalSnapped = snapFloorPoint(floorSnapped.position); setFloorPosition(point.pointUuid, finalSnapped.position); + } else if (point.pointType === 'Zone') { + const zoneSnapped = snapAisleAngle(newPosition); + const finalSnapped = snapZonePoint(zoneSnapped.position); + setZonePosition(point.pointUuid, finalSnapped.position); } } } @@ -199,6 +206,28 @@ function Point({ point }: { readonly point: Point }) { socket.emit('v1:model-Floor:add', data); }); } + } else if (point.pointType === 'Zone') { + const updatedZones = getZonesByPointId(point.pointUuid); + if (updatedZones && updatedZones.length > 0 && projectId) { + updatedZones.forEach((updatedZone) => { + + // API + + // upsertZoneApi(projectId, selectedVersion?.versionId || '', updatedZone); + + // SOCKET + + const data = { + zoneData: updatedZone, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:add', data); + }); + } } } @@ -289,6 +318,54 @@ function Point({ point }: { readonly point: Point }) { }); } } + if (point.pointType === 'Zone') { + const { removedZones, updatedZones } = removeZonePoint(point.pointUuid); + setHoveredPoint(null); + if (removedZones.length > 0) { + removedZones.forEach(zone => { + if (projectId) { + + // API + + // deleteZoneApi(projectId, selectedVersion?.versionId || '', zone.zoneUuid); + + // SOCKET + + const data = { + zoneUuid: zone.zoneUuid, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:delete', data); + } + }); + } + if (updatedZones.length > 0) { + updatedZones.forEach(zone => { + if (projectId) { + + // API + + // upsertZoneApi(projectId, selectedVersion?.versionId || '', zone); + + // SOCKET + + const data = { + zoneData: zone, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:add', data); + } + }); + } + } gl.domElement.style.cursor = 'default'; } } diff --git a/app/src/modules/builder/wall/Instances/instance/helpers/useWallClassification.ts b/app/src/modules/builder/wall/Instances/instance/helpers/useWallClassification.ts index 5ae9a21..bc910b5 100644 --- a/app/src/modules/builder/wall/Instances/instance/helpers/useWallClassification.ts +++ b/app/src/modules/builder/wall/Instances/instance/helpers/useWallClassification.ts @@ -85,9 +85,12 @@ export function useWallClassification(walls: Walls) { })); } + const allCoords = mergedLineStrings.flatMap(ls => ls.geometry.coordinates); + const uniqueCoords = Array.from(new Set(allCoords.map(coord => coord.join(',')))); + if (uniqueCoords.length < 4) return []; + const lineStrings = turf.featureCollection(mergedLineStrings); - // Now polygonize merged line strings const polygons = turf.polygonize(lineStrings); const rooms: Point[][] = []; diff --git a/app/src/modules/builder/zone/Instances/Instance/zoneInstance.tsx b/app/src/modules/builder/zone/Instances/Instance/zoneInstance.tsx index 5c29d5a..e49400c 100644 --- a/app/src/modules/builder/zone/Instances/Instance/zoneInstance.tsx +++ b/app/src/modules/builder/zone/Instances/Instance/zoneInstance.tsx @@ -37,8 +37,8 @@ function ZoneInstance({ zone }: { zone: Zone }) { name={`Zone-${zone.zoneUuid}`} userData={zone} > - {zone.points.slice(0, -1).map((point, index: number) => { - const nextPoint = zone.points[index + 1]; + {zone.points.map((point, index: number) => { + const nextPoint = zone.points[(index + 1) % zone.points.length]; const point1 = new Vector3(point.position[0], point.position[1], point.position[2]); const point2 = new Vector3(nextPoint.position[0], nextPoint.position[1], nextPoint.position[2]); diff --git a/app/src/modules/builder/zone/Instances/zoneInstances.tsx b/app/src/modules/builder/zone/Instances/zoneInstances.tsx index 48b426c..c1b9d64 100644 --- a/app/src/modules/builder/zone/Instances/zoneInstances.tsx +++ b/app/src/modules/builder/zone/Instances/zoneInstances.tsx @@ -13,10 +13,6 @@ function ZoneInstances() { const { zones } = zoneStore(); const { toggleView } = useToggleView(); - useEffect(() => { - console.log('zones: ', zones); - }, [zones]); - const allPoints = useMemo(() => { const points: Point[] = []; const seenUuids = new Set(); diff --git a/app/src/modules/builder/zone/zoneCreator/referenceZone.tsx b/app/src/modules/builder/zone/zoneCreator/referenceZone.tsx index 940134f..f4429bf 100644 --- a/app/src/modules/builder/zone/zoneCreator/referenceZone.tsx +++ b/app/src/modules/builder/zone/zoneCreator/referenceZone.tsx @@ -36,7 +36,7 @@ function ReferenceZone({ tempPoints }: Readonly) { setCurrentPosition([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z]); if (!intersectionPoint) return; - const snapped = snapZonePoint([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z], tempPoints.slice(0, -2)); + const snapped = snapZonePoint([intersectionPoint.x, intersectionPoint.y, intersectionPoint.z], [tempPoints[0]]); if (snapped.isSnapped && snapped.snappedPoint) { finalPosition.current = snapped.position; diff --git a/app/src/modules/builder/zone/zoneCreator/zoneCreator.tsx b/app/src/modules/builder/zone/zoneCreator/zoneCreator.tsx index af0fea2..1df200f 100644 --- a/app/src/modules/builder/zone/zoneCreator/zoneCreator.tsx +++ b/app/src/modules/builder/zone/zoneCreator/zoneCreator.tsx @@ -10,9 +10,250 @@ import { getUserData } from '../../../../functions/getUserData'; import ReferencePoint from '../../point/reference/referencePoint'; import ReferenceZone from './referenceZone'; +// import { upsertZoneApi } from '../../../../services/factoryBuilder/zone/upsertZoneApi'; + function ZoneCreator() { + const { scene, camera, raycaster, gl, pointer } = useThree(); + const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); + const { toggleView } = useToggleView(); + const { toolMode } = useToolMode(); + const { activeLayer } = useActiveLayer(); + const { socket } = useSocketStore(); + const { zoneStore } = useSceneContext(); + const { addZone, getZonePointById, getZoneByPoints } = zoneStore(); + const drag = useRef(false); + const isLeftMouseDown = useRef(false); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { userId, organization } = getUserData(); + const { projectId } = useParams(); + + const [tempPoints, setTempPoints] = useState([]); + const [isCreating, setIsCreating] = useState(false); + const { zoneColor, zoneHeight, snappedPosition, snappedPoint, setSnappedPoint, setSnappedPosition } = useBuilderStore(); + + useEffect(() => { + const canvasElement = gl.domElement; + + const onMouseDown = (evt: any) => { + if (evt.button === 0) { + isLeftMouseDown.current = true; + drag.current = false; + } + }; + + const onMouseUp = (evt: any) => { + if (evt.button === 0) { + isLeftMouseDown.current = false; + } + }; + + const onMouseMove = () => { + if (isLeftMouseDown) { + drag.current = true; + } + }; + + const onMouseClick = () => { + if (drag.current || !toggleView) return; + + raycaster.setFromCamera(pointer, camera); + const intersectionPoint = new THREE.Vector3(); + let position = raycaster.ray.intersectPlane(plane, intersectionPoint); + if (!position) return; + + const pointIntersects = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Zone-Point'); + + // const zoneIntersect = raycaster.intersectObjects(scene.children).find((intersect) => intersect.object.name === 'Zone-Line'); + + // if (zoneIntersect && !pointIntersects) { + + // } + + const newPoint: Point = { + pointUuid: THREE.MathUtils.generateUUID(), + pointType: 'Zone', + position: [position.x, position.y, position.z], + layer: activeLayer + }; + + if (snappedPosition && snappedPoint) { + newPoint.pointUuid = snappedPoint.pointUuid; + newPoint.position = snappedPosition; + newPoint.layer = snappedPoint.layer; + } + + if (snappedPoint && snappedPoint.pointUuid === tempPoints[tempPoints.length - 1]?.pointUuid) { return } + + if (snappedPosition && !snappedPoint) { + newPoint.position = snappedPosition; + } + + if (tempPoints.length > 2 && isCreating && snappedPoint && snappedPoint.pointUuid === tempPoints[0].pointUuid) { + const zone: Zone = { + zoneUuid: THREE.MathUtils.generateUUID(), + zoneName: "Zone", + points: tempPoints, + zoneColor, + zoneHeight, + viewPortPosition: [0, 0, 0], + viewPortTarget: [0, 0, 0] + }; + + addZone(zone); + if (projectId) { + + // API + + // upsertZoneApi(projectId, selectedVersion?.versionId || '', zone); + + // SOCKET + + const data = { + zoneData: zone, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:add', data); + + } + setTempPoints([]); + setIsCreating(false); + } else if (isCreating && snappedPoint && !tempPoints.some(p => p.pointUuid === snappedPoint.pointUuid)) { + setTempPoints(prev => [...prev, newPoint]); + setIsCreating(true); + } else if (pointIntersects) { + if (tempPoints.length > 2 && isCreating && pointIntersects.object.uuid === tempPoints[0].pointUuid) { + const zone: Zone = { + zoneUuid: THREE.MathUtils.generateUUID(), + zoneName: "Zone", + points: tempPoints, + zoneColor, + zoneHeight, + viewPortPosition: [0, 0, 0], + viewPortTarget: [0, 0, 0] + }; + + addZone(zone); + if (projectId) { + + // API + + // upsertZoneApi(projectId, selectedVersion?.versionId || '', zone); + + // SOCKET + + const data = { + zoneData: zone, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:add', data); + + } + setTempPoints([]); + setIsCreating(false); + } else if (tempPoints.length === 0 || (tempPoints.length > 1 && !tempPoints.slice(1).some(p => p.pointUuid === pointIntersects.object.uuid))) { + tempPoints.push(pointIntersects.object.userData as Point); + setIsCreating(true); + } + } else { + setTempPoints(prev => [...prev, newPoint]); + setIsCreating(true); + } + + }; + + const onContext = (event: any) => { + event.preventDefault(); + if (isCreating) { + if (tempPoints.length >= 3) { + const zone: Zone = { + zoneUuid: THREE.MathUtils.generateUUID(), + zoneName: "Zone", + points: tempPoints, + zoneColor, + zoneHeight, + viewPortPosition: [0, 0, 0], + viewPortTarget: [0, 0, 0] + }; + + addZone(zone); + if (projectId) { + + // API + + // upsertZoneApi(projectId, selectedVersion?.versionId || '', zone); + + // SOCKET + + const data = { + zoneData: zone, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:add', data); + + } + } + setTempPoints([]); + setIsCreating(false); + } + }; + + if (toolMode === "Zone" && toggleView) { + if (tempPoints.length === 0) { + setSnappedPosition(null); + setSnappedPoint(null); + } + canvasElement.addEventListener("mousedown", onMouseDown); + canvasElement.addEventListener("mouseup", onMouseUp); + canvasElement.addEventListener("mousemove", onMouseMove); + canvasElement.addEventListener("click", onMouseClick); + canvasElement.addEventListener("contextmenu", onContext); + } else { + setTempPoints([]); + setIsCreating(false); + canvasElement.removeEventListener("mousedown", onMouseDown); + canvasElement.removeEventListener("mouseup", onMouseUp); + canvasElement.removeEventListener("mousemove", onMouseMove); + canvasElement.removeEventListener("click", onMouseClick); + canvasElement.removeEventListener("contextmenu", onContext); + } + + return () => { + canvasElement.removeEventListener("mousedown", onMouseDown); + canvasElement.removeEventListener("mouseup", onMouseUp); + canvasElement.removeEventListener("mousemove", onMouseMove); + canvasElement.removeEventListener("click", onMouseClick); + canvasElement.removeEventListener("contextmenu", onContext); + }; + }, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addZone, getZonePointById, getZoneByPoints, zoneColor, zoneHeight, snappedPosition, snappedPoint]); + return ( <> + {toggleView && + <> + + {tempPoints.map((point) => ( + + ))} + + + {tempPoints.length > 0 && + + } + + } ) } diff --git a/app/src/services/factoryBuilder/zone/deleteZoneApi.ts b/app/src/services/factoryBuilder/zone/deleteZoneApi.ts index 1f2b63b..87b80dd 100644 --- a/app/src/services/factoryBuilder/zone/deleteZoneApi.ts +++ b/app/src/services/factoryBuilder/zone/deleteZoneApi.ts @@ -6,7 +6,7 @@ export const deleteZoneApi = async ( zoneUuid: string ) => { try { - const response = await fetch(`${url_Backend_dwinzo}/api/V2/deleteZone`, { + const response = await fetch(`${url_Backend_dwinzo}/api/V1/zones/delete`, { method: "PATCH", headers: { Authorization: "Bearer ", diff --git a/app/src/services/factoryBuilder/zone/getZonesApi.ts b/app/src/services/factoryBuilder/zone/getZonesApi.ts index df94fef..f0ea687 100644 --- a/app/src/services/factoryBuilder/zone/getZonesApi.ts +++ b/app/src/services/factoryBuilder/zone/getZonesApi.ts @@ -5,7 +5,7 @@ export const getZonesApi = async ( versionId: string, ) => { try { - const response = await fetch(`${url_Backend_dwinzo}/api/V2/zones/${projectId}/${versionId}`, { + const response = await fetch(`${url_Backend_dwinzo}/api/V1/zones/${projectId}/${versionId}`, { method: "GET", headers: { Authorization: "Bearer ", diff --git a/app/src/services/factoryBuilder/zone/upsertZoneApi.ts b/app/src/services/factoryBuilder/zone/upsertZoneApi.ts index eeab930..ffa79c5 100644 --- a/app/src/services/factoryBuilder/zone/upsertZoneApi.ts +++ b/app/src/services/factoryBuilder/zone/upsertZoneApi.ts @@ -1,12 +1,12 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; -export const upsertWallApi = async ( +export const upsertZoneApi = async ( projectId: string, versionId: string, - ZoneData: Zone + zoneData: Zone ) => { try { - const response = await fetch(`${url_Backend_dwinzo}/api/V2/UpsertZone`, { + const response = await fetch(`${url_Backend_dwinzo}/api/V1/UpsertZone`, { method: "POST", headers: { Authorization: "Bearer ", @@ -14,7 +14,7 @@ export const upsertWallApi = async ( token: localStorage.getItem("token") || "", refresh_token: localStorage.getItem("refreshToken") || "", }, - body: JSON.stringify({ projectId, versionId, ZoneData }), + body: JSON.stringify({ projectId, versionId, zoneData }), }); const newAccessToken = response.headers.get("x-access-token"); diff --git a/app/src/store/builder/store.ts b/app/src/store/builder/store.ts index e788670..860e618 100644 --- a/app/src/store/builder/store.ts +++ b/app/src/store/builder/store.ts @@ -473,6 +473,7 @@ export const useWidgetSubOption = create((set: any) => ({ widgetSubOption: "2D", setWidgetSubOption: (x: any) => set({ widgetSubOption: x }), })); + export const useLimitDistance = create((set: any) => ({ limitDistance: true, setLimitDistance: (x: any) => set({ limitDistance: x }), diff --git a/app/src/store/builder/useFloorStore.ts b/app/src/store/builder/useFloorStore.ts index 9f2d871..efb431b 100644 --- a/app/src/store/builder/useFloorStore.ts +++ b/app/src/store/builder/useFloorStore.ts @@ -257,8 +257,7 @@ export const createFloorStore = () => { return get().floors.find(floor => { const floorPointIds = new Set(floor.points.map(p => p.pointUuid)); const givenPointIds = new Set(points.map(p => p.pointUuid)); - return floorPointIds.size === givenPointIds.size && - [...floorPointIds].every(id => givenPointIds.has(id)); + return floorPointIds.size === givenPointIds.size && [...floorPointIds].every(id => givenPointIds.has(id)); }); }, diff --git a/app/src/store/builder/useZoneStore.ts b/app/src/store/builder/useZoneStore.ts index 148e88c..1a93e57 100644 --- a/app/src/store/builder/useZoneStore.ts +++ b/app/src/store/builder/useZoneStore.ts @@ -10,11 +10,19 @@ interface ZoneStore { setZoneHeight: (uuid: string, height: number) => void; setZoneColor: (uuid: string, color: string) => void; removeZone: (uuid: string) => void; - removePointFromZones: (pointUuid: string) => void; + removePoint: (pointUuid: string) => { removedZones: Zone[], updatedZones: Zone[] }; + removeZoneByPoints: (points: Point[]) => { removedZones: Zone[], updatedZones: Zone[] }; clearZones: () => void; + setPosition: ( + pointUuid: string, + position: [number, number, number] + ) => Zone[] | []; setViewPort: (uuid: string, position: [number, number, number], target: [number, number, number]) => void; getZoneById: (uuid: string) => Zone | undefined; + getZonesByPointId: (uuid: string) => Zone[] | []; + getZoneByPoints: (points: Point[]) => Zone | undefined; + getZonePointById: (uuid: string) => Point | undefined; getConnectedPoints: (uuid: string) => Point[]; } @@ -63,16 +71,97 @@ export const createZoneStore = () => { state.zones = state.zones.filter(z => z.zoneUuid !== uuid); }), - removePointFromZones: (pointUuid) => set(state => { - for (const zone of state.zones) { - zone.points = zone.points.filter(p => p.pointUuid !== pointUuid); - } - }), + removePoint: (pointUuid) => { + const removedZones: Zone[] = []; + const updatedZones: Zone[] = []; + + set(state => { + for (const zone of state.zones) { + const pointIndex = zone.points.findIndex(p => p.pointUuid === pointUuid); + if (pointIndex === -1) { + updatedZones.push(JSON.parse(JSON.stringify(zone))); + continue; + } + + const remainingPoints = zone.points.filter(p => p.pointUuid !== pointUuid); + + if (remainingPoints.length <= 2) { + removedZones.push(JSON.parse(JSON.stringify(zone))); + continue; + } + zone.points = remainingPoints; + updatedZones.push(JSON.parse(JSON.stringify(zone))); + } + + state.zones = updatedZones; + }); + + return { removedZones, updatedZones }; + }, + + removeZoneByPoints: ([pointA, pointB]) => { + const removedZones: Zone[] = []; + const updatedZones: Zone[] = []; + + set(state => { + + for (const zone of state.zones) { + const indices = zone.points.map((p, i) => ({ uuid: p.pointUuid, index: i })); + + const idxA = indices.find(i => i.uuid === pointA.pointUuid)?.index ?? -1; + const idxB = indices.find(i => i.uuid === pointB.pointUuid)?.index ?? -1; + + if (idxA === -1 || idxB === -1) { + updatedZones.push(JSON.parse(JSON.stringify(zone))); + continue; + } + + const areAdjacent = + Math.abs(idxA - idxB) === 1 || + (idxA === 0 && idxB === zone.points.length - 1) || + (idxB === 0 && idxA === zone.points.length - 1); + + if (!areAdjacent) { + updatedZones.push(JSON.parse(JSON.stringify(zone))); + continue; + } + + const remainingPoints = zone.points.filter( + p => p.pointUuid !== pointA.pointUuid && p.pointUuid !== pointB.pointUuid + ); + + if (remainingPoints.length > 2) { + zone.points = remainingPoints; + updatedZones.push(JSON.parse(JSON.stringify(zone))); + } else { + removedZones.push(JSON.parse(JSON.stringify(zone))); + } + } + + state.zones = updatedZones; + }); + + return { removedZones, updatedZones }; + }, clearZones: () => set(state => { state.zones = []; }), + setPosition: (pointUuid, position) => { + let updatedZone: Zone[] = []; + set((state) => { + for (const zone of state.zones) { + const point = zone.points.find((p) => p.pointUuid === pointUuid); + if (point) { + point.position = position; + updatedZone.push(JSON.parse(JSON.stringify(zone))); + } + } + }); + return updatedZone; + }, + setViewPort: (uuid, position, target) => set(state => { const zone = state.zones.find(z => z.zoneUuid === uuid); if (zone) { @@ -85,6 +174,28 @@ export const createZoneStore = () => { return get().zones.find(z => z.zoneUuid === uuid); }, + getZonesByPointId: (pointUuid) => { + return get().zones.filter(zone => { + return zone.points.some(p => p.pointUuid === pointUuid); + }); + }, + + getZoneByPoints: (points) => { + return get().zones.find(zone => { + const zonePointIds = new Set(zone.points.map(p => p.pointUuid)); + const givenPointIds = new Set(points.map(p => p.pointUuid)); + return zonePointIds.size === givenPointIds.size && [...zonePointIds].every(id => givenPointIds.has(id)); + }); + }, + + getZonePointById: (pointUuid) => { + for (const zone of get().zones) { + const point = zone.points.find(p => p.pointUuid === pointUuid); + if (point) return point; + } + return undefined; + }, + getConnectedPoints: (pointUuid) => { const connected: Point[] = []; for (const zone of get().zones) {