import * as THREE from 'three'; import { useThree } from '@react-three/fiber'; import { useEffect, useMemo, useState } from "react"; import { DragControls, Tube } from '@react-three/drei'; import { useSocketStore, useToolMode } from '../../../store/builder/store'; import { useBuilderStore } from '../../../store/builder/useBuilderStore'; import { useSceneContext } from '../../scene/sceneContext'; import * as Constants from '../../../types/world/worldConstants'; import { deleteWallApi } from '../../../services/factoryBuilder/wall/deleteWallApi'; import { useVersionContext } from '../version/versionContext'; import { useParams } from 'react-router-dom'; import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi'; import { getUserData } from '../../../functions/getUserData'; interface LineProps { points: [Point, Point]; } function Line({ points }: Readonly) { const [isHovered, setIsHovered] = useState(false); const { raycaster, camera, pointer, gl } = useThree(); const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); const [isDeletable, setIsDeletable] = useState(false); const { socket } = useSocketStore(); const { toolMode } = useToolMode(); const { wallStore, floorStore } = useSceneContext(); const { removeWallByPoints, setPosition: setWallPosition, getWallsByPointId } = wallStore(); const { removeFloorByPoints, setPosition: setFloorPosition } = floorStore(); const { userId, organization } = getUserData(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); const { projectId } = useParams(); const [dragOffset, setDragOffset] = useState(null); const { hoveredLine, setHoveredLine, hoveredPoint } = useBuilderStore(); const path = useMemo(() => { const [start, end] = points.map(p => new THREE.Vector3(...p.position)); return new THREE.LineCurve3(start, end); }, [points]); const colors = getColor(points[0]); function getColor(point: Point) { if (point.pointType === 'Aisle') { return { defaultLineColor: Constants.lineConfig.aisleColor, defaultDeleteColor: Constants.lineConfig.deleteColor, } } else if (point.pointType === 'Floor') { return { defaultLineColor: Constants.lineConfig.floorColor, defaultDeleteColor: Constants.lineConfig.deleteColor, } } else if (point.pointType === 'Wall') { return { defaultLineColor: Constants.lineConfig.wallColor, defaultDeleteColor: Constants.lineConfig.deleteColor, } } else if (point.pointType === 'Zone') { return { defaultLineColor: Constants.lineConfig.zoneColor, defaultDeleteColor: Constants.lineConfig.deleteColor, } } else { return { defaultLineColor: Constants.lineConfig.defaultColor, defaultDeleteColor: Constants.lineConfig.deleteColor, } } } useEffect(() => { if (toolMode === '2D-Delete') { if (isHovered && !hoveredPoint) { setIsDeletable(true); } else { setIsDeletable(false); } } else { setIsDeletable(false); } }, [isHovered, colors.defaultLineColor, colors.defaultDeleteColor, toolMode, hoveredPoint]); useEffect(() => { if (hoveredLine && (hoveredLine[0].pointUuid !== points[0].pointUuid || hoveredLine[1].pointUuid !== points[1].pointUuid)) { setIsHovered(false); } }, [hoveredLine]) const handlePointClick = (points: [Point, Point]) => { if (toolMode === '2D-Delete') { if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') { const removedWall = removeWallByPoints(points); if (removedWall && projectId) { // API // deleteWallApi(projectId, selectedVersion?.versionId || '', removedWall.wallUuid); // SOCKET const data = { wallUuid: removedWall.wallUuid, projectId: projectId, versionId: selectedVersion?.versionId || '', userId: userId, organization: organization } socket.emit('v1:model-Wall:delete', data); } setHoveredLine(null); } if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') { removeFloorByPoints(points); setHoveredLine(null); } gl.domElement.style.cursor = 'default'; } } const handleDrag = (points: [Point, Point]) => { if (toolMode === 'move' && isHovered && dragOffset) { raycaster.setFromCamera(pointer, camera); const intersectionPoint = new THREE.Vector3(); const hit = raycaster.ray.intersectPlane(plane, intersectionPoint); if (hit) { gl.domElement.style.cursor = 'move'; const positionWithOffset = new THREE.Vector3().addVectors(hit, dragOffset); const start = new THREE.Vector3(...points[0].position); const end = new THREE.Vector3(...points[1].position); const midPoint = new THREE.Vector3().addVectors(start, end).multiplyScalar(0.5); const delta = new THREE.Vector3().subVectors(positionWithOffset, midPoint); const newStart = new THREE.Vector3().addVectors(start, delta); const newEnd = new THREE.Vector3().addVectors(end, delta); if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') { setWallPosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]); setWallPosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]); } if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') { setFloorPosition(points[0].pointUuid, [newStart.x, newStart.y, newStart.z]); setFloorPosition(points[1].pointUuid, [newEnd.x, newEnd.y, newEnd.z]); } } } }; const handleDragStart = (points: [Point, Point]) => { raycaster.setFromCamera(pointer, camera); const intersectionPoint = new THREE.Vector3(); const hit = raycaster.ray.intersectPlane(plane, intersectionPoint); if (hit && !hoveredPoint) { const start = new THREE.Vector3(...points[0].position); const end = new THREE.Vector3(...points[1].position); const midPoint = new THREE.Vector3().addVectors(start, end).multiplyScalar(0.5); const offset = new THREE.Vector3().subVectors(midPoint, hit); setDragOffset(offset); } }; const handleDragEnd = (points: [Point, Point]) => { if (toolMode !== 'move' || !dragOffset) return; gl.domElement.style.cursor = 'default'; setDragOffset(null); if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') { const updatedWalls1 = getWallsByPointId(points[0].pointUuid); const updatedWalls2 = getWallsByPointId(points[1].pointUuid); const updatedWalls = [...updatedWalls1, ...updatedWalls2].filter((wall, index, self) => index === self.findIndex((w) => w.wallUuid === wall.wallUuid)); if (updatedWalls.length > 0 && projectId) { updatedWalls.forEach(updatedWall => { // API // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall).catch((error) => { // console.error('Error updating wall:', error); // }); // SOCKET const data = { wallData: updatedWall, projectId: projectId, versionId: selectedVersion?.versionId || '', userId: userId, organization: organization } socket.emit('v1:model-Wall:add', data); }) } } else if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') { // Handle floor update logic here if needed } } return ( handleDragStart(points)} onDrag={() => handleDrag(points)} onDragEnd={() => handleDragEnd(points)} > { handlePointClick(points); }} onPointerOver={() => { if (!hoveredLine) { setHoveredLine(points); setIsHovered(true) if (toolMode === 'move' && !hoveredPoint) { gl.domElement.style.cursor = 'pointer'; } } }} onPointerOut={() => { if (hoveredLine) { setHoveredLine(null); gl.domElement.style.cursor = 'default'; } setIsHovered(false) }} > ); } export default Line;