import * as THREE from 'three'; import * as Constants from '../../../types/world/worldConstants'; import { useRef, useState, useEffect, useMemo } from 'react'; import { useToolMode } from '../../../store/builder/store'; import { DragControls } from '@react-three/drei'; import { useThree } from '@react-three/fiber'; import { useBuilderStore } from '../../../store/builder/useBuilderStore'; import { usePointSnapping } from './helpers/usePointSnapping'; import { deleteAisleApi } from '../../../services/factoryBuilder/aisle/deleteAisleApi'; import { useParams } from 'react-router-dom'; import { createAisleApi } from '../../../services/factoryBuilder/aisle/createAisleApi'; import { useVersionContext } from '../version/versionContext'; import { useSceneContext } from '../../scene/sceneContext'; function Point({ point }: { readonly point: Point }) { const materialRef = useRef(null); const { raycaster, camera, pointer, gl } = useThree(); const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); const [isHovered, setIsHovered] = useState(false); const [dragOffset, setDragOffset] = useState(null); const { toolMode } = useToolMode(); const { aisleStore, wallStore } = useSceneContext(); const { setPosition: setAislePosition, removePoint: removeAislePoint, getAislesByPointId } = aisleStore(); const { setPosition: setWallPosition, removePoint: removeWallPoint } = wallStore(); const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position }); const { hoveredPoint, setHoveredPoint } = useBuilderStore(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); const { projectId } = useParams(); const boxScale: [number, number, number] = Constants.pointConfig.boxScale; const colors = getColor(point); useEffect(() => { gl.domElement.style.cursor = 'default'; }, [toolMode]) function getColor(point: Point) { if (point.pointType === 'Aisle') { return { defaultInnerColor: Constants.pointConfig.defaultInnerColor, defaultOuterColor: Constants.pointConfig.aisleOuterColor, defaultDeleteColor: Constants.pointConfig.deleteColor, } } else if (point.pointType === 'Floor') { return { defaultInnerColor: Constants.pointConfig.defaultInnerColor, defaultOuterColor: Constants.pointConfig.floorOuterColor, defaultDeleteColor: Constants.pointConfig.deleteColor, } } else if (point.pointType === 'Wall') { return { defaultInnerColor: Constants.pointConfig.defaultInnerColor, defaultOuterColor: Constants.pointConfig.wallOuterColor, defaultDeleteColor: Constants.pointConfig.deleteColor, } } else if (point.pointType === 'Zone') { return { defaultInnerColor: Constants.pointConfig.defaultInnerColor, defaultOuterColor: Constants.pointConfig.zoneOuterColor, defaultDeleteColor: Constants.pointConfig.deleteColor, } } else { return { defaultInnerColor: Constants.pointConfig.defaultInnerColor, defaultOuterColor: Constants.pointConfig.defaultOuterColor, defaultDeleteColor: Constants.pointConfig.deleteColor, } } } useEffect(() => { if (materialRef.current && (toolMode === 'move' || toolMode === '2D-Delete')) { let innerColor; let outerColor; if (isHovered) { innerColor = toolMode === '2D-Delete' ? colors.defaultDeleteColor : colors.defaultOuterColor; outerColor = toolMode === '2D-Delete' ? colors.defaultDeleteColor : colors.defaultOuterColor; } else { innerColor = colors.defaultInnerColor; outerColor = colors.defaultOuterColor; } materialRef.current.uniforms.uInnerColor.value.set(innerColor); materialRef.current.uniforms.uOuterColor.value.set(outerColor); materialRef.current.uniformsNeedUpdate = true; } else if (materialRef.current && toolMode !== 'move') { materialRef.current.uniforms.uInnerColor.value.set(colors.defaultInnerColor); materialRef.current.uniforms.uOuterColor.value.set(colors.defaultOuterColor); materialRef.current.uniformsNeedUpdate = true; } }, [isHovered, colors.defaultInnerColor, colors.defaultOuterColor, colors.defaultDeleteColor, toolMode]); const uniforms = useMemo(() => ({ uOuterColor: { value: new THREE.Color(colors.defaultOuterColor) }, uInnerColor: { value: new THREE.Color(colors.defaultInnerColor) }, }), [colors.defaultInnerColor, colors.defaultOuterColor]); const handleDrag = (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 newPosition: [number, number, number] = [positionWithOffset.x, positionWithOffset.y, positionWithOffset.z]; if (point.pointType === 'Aisle') { const aisleSnapped = snapAisleAngle(newPosition); const finalSnapped = snapAislePoint(aisleSnapped.position); setAislePosition(point.pointUuid, finalSnapped.position); } else if (point.pointType === 'Wall') { const wallSnapped = snapWallAngle(newPosition); const finalSnapped = snapWallPoint(wallSnapped.position); setWallPosition(point.pointUuid, finalSnapped.position); } } } }; const handleDragStart = (point: Point) => { raycaster.setFromCamera(pointer, camera); const intersectionPoint = new THREE.Vector3(); const hit = raycaster.ray.intersectPlane(plane, intersectionPoint); if (hit) { const currentPosition = new THREE.Vector3(...point.position); const offset = new THREE.Vector3().subVectors(currentPosition, hit); setDragOffset(offset); } }; const handleDragEnd = (point: Point) => { gl.domElement.style.cursor = 'default'; setDragOffset(null); if (toolMode !== 'move') return; if (point.pointType === 'Aisle') { const updatedAisles = getAislesByPointId(point.pointUuid); if (updatedAisles.length > 0 && projectId) { updatedAisles.forEach((updatedAisle) => { createAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || '') }) } } else if (point.pointType === 'Wall') { // console.log('Wall after drag: ', point); } } const handlePointClick = (point: Point) => { if (toolMode === '2D-Delete') { if (point.pointType === 'Aisle') { const removedAisles = removeAislePoint(point.pointUuid); if (removedAisles.length > 0) { removedAisles.forEach(aisle => { if (projectId) deleteAisleApi(aisle.aisleUuid, projectId, selectedVersion?.versionId || '') }); setHoveredPoint(null); } } if (point.pointType === 'Wall') { const removedAisles = removeWallPoint(point.pointUuid); if (removedAisles.length > 0) { setHoveredPoint(null); } } gl.domElement.style.cursor = 'default'; } } useEffect(() => { if (hoveredPoint && hoveredPoint.pointUuid !== point.pointUuid) { setIsHovered(false); } }, [hoveredPoint]) if (!point) { return null; } return ( handleDragStart(point)} onDrag={() => handleDrag(point)} onDragEnd={() => handleDragEnd(point)} > { handlePointClick(point); }} onPointerOver={() => { if (!hoveredPoint) { setHoveredPoint(point); setIsHovered(true); if (toolMode === 'move') { gl.domElement.style.cursor = 'pointer'; } } }} onPointerOut={() => { if (hoveredPoint) { setHoveredPoint(null); gl.domElement.style.cursor = 'default'; } setIsHovered(false) }} userData={point} > borderThickness && vUv.x < 1.0 - borderThickness && vUv.y > borderThickness && vUv.y < 1.0 - borderThickness) { gl_FragColor = vec4(uInnerColor, 1.0); // Inner square } else { gl_FragColor = vec4(uOuterColor, 1.0); // Border } } ` } /> ); } export default Point;