From c0e040fb3ab9e25f1652e392e06e1911e5236602 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 26 Aug 2025 16:38:29 +0530 Subject: [PATCH] added decal movement across the wall --- .../properties/SelectedDecalProperties.tsx | 20 ++-- .../modules/builder/Decal/decalInstance.tsx | 104 ++++++++++++++++-- .../calculateAssetTransformationOnWall.ts | 4 +- .../Instances/Instance/wallAssetInstance.tsx | 35 ++---- app/src/store/builder/useWallStore.ts | 23 ++-- 5 files changed, 131 insertions(+), 55 deletions(-) diff --git a/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx b/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx index 7994a1f..99c16da 100644 --- a/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/SelectedDecalProperties.tsx @@ -14,8 +14,8 @@ import { getUserData } from "../../../../functions/getUserData"; const SelectedDecalProperties = () => { const { selectedDecal, setSelectedDecal } = useBuilderStore(); const { wallStore, floorStore } = useSceneContext(); - const { updateDecal: updateDecalFromWall } = wallStore(); - const { updateDecal: updateDecalFromFloor } = floorStore(); + const { updateDecal: updateDecalInWall } = wallStore(); + const { updateDecal: updateDecalInFloor } = floorStore(); const { userId, organization } = getUserData(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); @@ -68,10 +68,10 @@ const SelectedDecalProperties = () => { setSelectedDecal({ ...selectedDecal, decalData: updatedDecal }); if ('wallUuid' in selectedDecal.decalData.decalType) { - const updatedWall = updateDecalFromWall(updatedDecal.decalUuid, updatedDecal); + const updatedWall = updateDecalInWall(updatedDecal.decalUuid, updatedDecal); if (updatedWall) updateBackend(updatedWall); } else if ('floorUuid' in selectedDecal.decalData.decalType) { - const updatedFloor = updateDecalFromFloor(updatedDecal.decalUuid, updatedDecal); + const updatedFloor = updateDecalInFloor(updatedDecal.decalUuid, updatedDecal); if (updatedFloor) updateBackend(updatedFloor); } } @@ -82,10 +82,10 @@ const SelectedDecalProperties = () => { setSelectedDecal({ ...selectedDecal, decalData: updatedDecal }); if ('wallUuid' in selectedDecal.decalData.decalType) { - const updatedWall = updateDecalFromWall(updatedDecal.decalUuid, updatedDecal); + const updatedWall = updateDecalInWall(updatedDecal.decalUuid, updatedDecal); if (updatedWall) updateBackend(updatedWall); } else if ('floorUuid' in selectedDecal.decalData.decalType) { - const updatedFloor = updateDecalFromFloor(updatedDecal.decalUuid, updatedDecal); + const updatedFloor = updateDecalInFloor(updatedDecal.decalUuid, updatedDecal); if (updatedFloor) updateBackend(updatedFloor); } } @@ -96,10 +96,10 @@ const SelectedDecalProperties = () => { setSelectedDecal({ ...selectedDecal, decalData: updatedDecal }); if ('wallUuid' in selectedDecal.decalData.decalType) { - const updatedWall = updateDecalFromWall(updatedDecal.decalUuid, updatedDecal); + const updatedWall = updateDecalInWall(updatedDecal.decalUuid, updatedDecal); if (updatedWall) updateBackend(updatedWall); } else if ('floorUuid' in selectedDecal.decalData.decalType) { - const updatedFloor = updateDecalFromFloor(updatedDecal.decalUuid, updatedDecal); + const updatedFloor = updateDecalInFloor(updatedDecal.decalUuid, updatedDecal); if (updatedFloor) updateBackend(updatedFloor); } } @@ -120,10 +120,10 @@ const SelectedDecalProperties = () => { setSelectedDecal({ ...selectedDecal, decalData: updatedDecal }); if ("wallUuid" in selectedDecal.decalData.decalType) { - const updatedWall = updateDecalFromWall(updatedDecal.decalUuid, updatedDecal); + const updatedWall = updateDecalInWall(updatedDecal.decalUuid, updatedDecal); if (updatedWall) updateBackend(updatedWall); } else if ("floorUuid" in selectedDecal.decalData.decalType) { - const updatedFloor = updateDecalFromFloor(updatedDecal.decalUuid, updatedDecal); + const updatedFloor = updateDecalInFloor(updatedDecal.decalUuid, updatedDecal); if (updatedFloor) updateBackend(updatedFloor); } }; diff --git a/app/src/modules/builder/Decal/decalInstance.tsx b/app/src/modules/builder/Decal/decalInstance.tsx index 59f2141..d414419 100644 --- a/app/src/modules/builder/Decal/decalInstance.tsx +++ b/app/src/modules/builder/Decal/decalInstance.tsx @@ -1,13 +1,13 @@ import * as THREE from 'three'; -import { Decal } from '@react-three/drei' -import { useLoader } from '@react-three/fiber'; +import { CameraControls, Decal } from '@react-three/drei' +import { useLoader, useThree } from '@react-three/fiber'; import { useSocketStore, useToggleView, useToolMode } from '../../../store/builder/store'; import { useBuilderStore } from '../../../store/builder/useBuilderStore'; import defaultMaterial from '../../../assets/textures/floor/wall-tex.png'; import useModuleStore from '../../../store/useModuleStore'; import { useSceneContext } from '../../scene/sceneContext'; -import { useEffect } from 'react'; +import { useEffect, useRef } from 'react'; import { getUserData } from '../../../functions/getUserData'; import { useVersionContext } from '../version/versionContext'; import { useParams } from 'react-router-dom'; @@ -18,8 +18,8 @@ import { useParams } from 'react-router-dom'; function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalPosition[2] }: { parent: Wall | Floor; visible?: boolean, decal: Decal, zPosition?: number }) { const { setSelectedWall, setSelectedFloor, selectedDecal, deletableDecal, setSelectedDecal, setDeletableDecal } = useBuilderStore(); const { wallStore, floorStore } = useSceneContext(); - const { removeDecal: removeDecalFromWall } = wallStore(); - const { removeDecal: removeDecalFromFloor } = floorStore(); + const { removeDecal: removeDecalInWall, updateDecalPosition: updateDecalPositionInWall, getWallById } = wallStore(); + const { removeDecal: removeDecalInFloor } = floorStore(); const { toolMode } = useToolMode(); const { toggleView } = useToggleView(); const { activeModule } = useModuleStore(); @@ -30,6 +30,10 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP const { socket } = useSocketStore(); const material = useLoader(THREE.TextureLoader, defaultMaterial); + const { raycaster, pointer, camera, scene, gl, controls } = useThree(); + const isDraggingRef = useRef(false); + const dragOffsetRef = useRef(null); + useEffect(() => { if (!toggleView && activeModule === 'builder') { if (toolMode !== 'cursor') { @@ -44,9 +48,77 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP } }, [toggleView, toolMode, activeModule, selectedDecal, deletableDecal]); + useEffect(() => { + const canvasElement = gl.domElement; + + const handlePointerMove = (e: PointerEvent) => { + if (!isDraggingRef.current || !selectedDecal || selectedDecal.decalData.decalUuid !== decal.decalUuid) return; + + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster.intersectObjects(scene.children, true); + + const wallIntersect = intersects.find(i => i.object.userData && 'wallUuid' in parent && i.object.userData.wallUuid === parent.wallUuid); + + if (wallIntersect) { + const point = wallIntersect.object.worldToLocal(wallIntersect.point.clone()); + + let offset = dragOffsetRef.current || new THREE.Vector3(0, 0, 0); + + updateDecalPositionInWall(decal.decalUuid, [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]]); + } + }; + + const handlePointerUp = (e: PointerEvent) => { + if (controls) { + (controls as CameraControls).enabled = true; + } + if (isDraggingRef.current) { + isDraggingRef.current = false; + dragOffsetRef.current = null; + + if ('wallUuid' in parent) { + setTimeout(() => { + const updatedWall = getWallById(parent.wallUuid); + + if (updatedWall) { + if (projectId && updatedWall) { + // API + + // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall); + + // SOCKET + + const data = { + wallData: updatedWall, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + } + } + }, 0) + } + + } + }; + + if (activeModule === 'builder' && !toggleView) { + canvasElement.addEventListener('pointermove', handlePointerMove); + canvasElement.addEventListener('pointerup', handlePointerUp); + } + + return () => { + canvasElement.removeEventListener('pointermove', handlePointerMove); + canvasElement.removeEventListener('pointerup', handlePointerUp); + }; + }, [gl, camera, scene, raycaster, selectedDecal, decal, parent, activeModule, toggleView, projectId, selectedVersion, userId, organization, socket]); + const deleteDecal = (decalUuid: string, parent: Wall | Floor) => { if ('wallUuid' in parent) { - const updatedWall = removeDecalFromWall(decalUuid); + const updatedWall = removeDecalInWall(decalUuid); if (projectId && updatedWall) { // API @@ -66,7 +138,7 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP socket.emit('v1:model-Wall:add', data); } } else if ('floorUuid' in parent) { - const updatedFloor = removeDecalFromFloor(decalUuid); + const updatedFloor = removeDecalInFloor(decalUuid); if (projectId && updatedFloor) { // API @@ -96,6 +168,24 @@ function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalP rotation={[0, 0, decal.decalRotation * (Math.PI / 180)]} scale={[decal.decalScale, decal.decalScale, 0.01]} userData={decal} + onPointerDown={(e) => { + if (visible && !toggleView && activeModule === 'builder') { + if (e.object.userData.decalUuid && toolMode === 'cursor') { + e.stopPropagation(); + isDraggingRef.current = true; + if (controls) { + (controls as CameraControls).enabled = false; + } + setSelectedDecal({ decalMesh: e.object, decalData: decal }); + setSelectedWall(null); + setSelectedFloor(null); + + const localIntersect = e.object.worldToLocal(e.point.clone()); + dragOffsetRef.current = new THREE.Vector3(decal.decalPosition[0] - localIntersect.x, decal.decalPosition[1] - localIntersect.y, 0); + } + } + }} + onClick={(e) => { if (visible && !toggleView && activeModule === 'builder') { if (e.object.userData.decalUuid) { diff --git a/app/src/modules/builder/wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall.ts b/app/src/modules/builder/wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall.ts index aa881c8..89142d5 100644 --- a/app/src/modules/builder/wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall.ts +++ b/app/src/modules/builder/wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall.ts @@ -18,10 +18,10 @@ const calculateAssetTransformationOnWall = ( const projection = initialWallNormalized.clone().multiplyScalar(dotProduct); const perpendicular = new THREE.Vector3().subVectors(assetVector, projection); - const distanceFromWall = perpendicular.length(); + const distanceInWall = perpendicular.length(); const crossProduct = new THREE.Vector3().crossVectors(initialWallNormalized, perpendicular).y; - const signedDistance = distanceFromWall * (crossProduct >= 0 ? 1 : -1); + const signedDistance = distanceInWall * (crossProduct >= 0 ? 1 : -1); const percentage = Math.max(0, Math.min(1, dotProduct / initialWallLength)); diff --git a/app/src/modules/builder/wallAsset/Instances/Instance/wallAssetInstance.tsx b/app/src/modules/builder/wallAsset/Instances/Instance/wallAssetInstance.tsx index 76b3ad7..3919f65 100644 --- a/app/src/modules/builder/wallAsset/Instances/Instance/wallAssetInstance.tsx +++ b/app/src/modules/builder/wallAsset/Instances/Instance/wallAssetInstance.tsx @@ -23,7 +23,7 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) { const { raycaster, pointer, camera, scene, controls, gl } = useThree(); const { wallStore, wallAssetStore } = useSceneContext(); const { walls, getWallById } = wallStore(); - const { updateWallAsset, removeWallAsset } = wallAssetStore(); + const { updateWallAsset, removeWallAsset, getWallAssetById } = wallAssetStore(); const { toggleView } = useToggleView(); const { activeTool } = useActiveTool(); const { activeModule } = useModuleStore(); @@ -116,33 +116,14 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) { const canvasElement = gl.domElement; const onPointerUp = (e: PointerEvent) => { - draggingRef.current = false; - if (controls) { - (controls as any).enabled = true; - } + if (draggingRef.current) { + draggingRef.current = false; + if (controls) { + (controls as any).enabled = true; + } - if (selectedWallAsset) { - pointer.x = (e.clientX / window.innerWidth) * 2 - 1; - pointer.y = -(e.clientY / window.innerHeight) * 2 + 1; - - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster.intersectObjects(scene.children, true); - const intersect = intersects.find((i: any) => i.object.name.includes('WallReference')); - - if (intersect && intersect.object.userData.wallUuid && selectedWallAsset.userData.modelUuid === wallAsset.modelUuid) { - const newPoint = closestPointOnLineSegment( - new THREE.Vector3(intersect.point.x, 0, intersect.point.z), - new THREE.Vector3(...intersect.object.userData.points[0].position), - new THREE.Vector3(...intersect.object.userData.points[1].position) - ); - - const wallRotation = intersect.object.rotation.clone(); - - const updatedWallAsset = updateWallAsset(wallAsset.modelUuid, { - wallUuid: intersect.object.userData.wallUuid, - position: [newPoint.x, wallAsset.wallAssetType === 'fixedMove' ? 0 : intersect.point.y, newPoint.z], - rotation: [wallRotation.x, wallRotation.y, wallRotation.z], - }); + if (selectedWallAsset) { + const updatedWallAsset = getWallAssetById(wallAsset.modelUuid); if (projectId && updatedWallAsset) { diff --git a/app/src/store/builder/useWallStore.ts b/app/src/store/builder/useWallStore.ts index d1b2e86..c7c07e9 100644 --- a/app/src/store/builder/useWallStore.ts +++ b/app/src/store/builder/useWallStore.ts @@ -12,7 +12,7 @@ interface WallStore { addDecal: (wallUuid: string, decal: Decal) => void; updateDecal: (decalUuid: string, decal: Decal) => Wall | undefined; removeDecal: (decalUuid: string) => Wall | undefined; - updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void; + updateDecalPosition: (decalUuid: string, position: [number, number, number]) => Wall | undefined; updateDecalRotation: (decalUuid: string, rotation: number) => void; updateDecalScale: (decalUuid: string, scale: number) => void; @@ -118,15 +118,20 @@ export const createWallStore = () => { return affectedWall; }, - updateDecalPosition: (decalUuid, position) => set((state) => { - for (const wall of state.walls) { - const decal = wall.decals.find(d => d.decalUuid === decalUuid); - if (decal) { - decal.decalPosition = position; - break; + updateDecalPosition: (decalUuid, position) => { + let affectedWall: Wall | undefined; + set((state) => { + for (const wall of state.walls) { + const decal = wall.decals.find(d => d.decalUuid === decalUuid); + if (decal) { + decal.decalPosition = position; + affectedWall = JSON.parse(JSON.stringify(wall)); + break; + } } - } - }), + }) + return affectedWall; + }, updateDecalRotation: (decalUuid, rotation) => set((state) => { for (const wall of state.walls) {