added event handler for wall asset

This commit is contained in:
2025-09-05 11:32:25 +05:30
parent 1d7b3b974d
commit 33fb90af71
2 changed files with 223 additions and 200 deletions

View File

@@ -0,0 +1,213 @@
import * as THREE from 'three';
import { useCallback, useEffect, useRef } from 'react';
import { ThreeEvent, useThree } from '@react-three/fiber';
import { useParams } from 'react-router-dom';
import { useActiveTool, useSocketStore, useToggleView } from '../../../../../../store/builder/store';
import useModuleStore from '../../../../../../store/useModuleStore';
import { useSceneContext } from '../../../../../scene/sceneContext';
import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore';
import { useVersionContext } from '../../../../version/versionContext';
import { getUserData } from '../../../../../../functions/getUserData';
import closestPointOnLineSegment from '../../../../line/helpers/getClosestPointOnLineSegment';
import { upsertWallAssetApi } from '../../../../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi';
import { deleteWallAssetApi } from '../../../../../../services/factoryBuilder/asset/wallAsset/deleteWallAssetApi';
export function useWallAssetEventHandler({
wallAsset,
groupRef,
wall,
}: {
wallAsset: WallAsset;
groupRef: React.RefObject<THREE.Group>;
wall: Wall | undefined;
}) {
const { socket } = useSocketStore();
const { raycaster, pointer, camera, scene, controls, gl } = useThree();
const { wallAssetStore } = useSceneContext();
const { updateWallAsset, removeWallAsset, getWallAssetById } = wallAssetStore();
const { toggleView } = useToggleView();
const { activeTool } = useActiveTool();
const { activeModule } = useModuleStore();
const { selectedWallAsset, setSelectedWallAsset, setDeletableWallAsset, deletableWallAsset } = useBuilderStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { userId, organization } = getUserData();
const { projectId } = useParams();
const draggingRef = useRef(false);
useEffect(() => {
const canvasElement = gl.domElement;
const onPointerUp = (e: PointerEvent) => {
if (draggingRef.current) {
draggingRef.current = false;
if (controls) {
(controls as any).enabled = true;
}
if (selectedWallAsset) {
const updatedWallAsset = getWallAssetById(wallAsset.modelUuid);
if (projectId && updatedWallAsset) {
if (!socket?.connected) {
// API
upsertWallAssetApi(projectId, selectedVersion?.versionId || '', updatedWallAsset);
} else {
// SOCKET
const data = {
wallAssetData: updatedWallAsset,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:wall-asset:add', data);
}
}
}
}
};
const onPointerMove = (e: any) => {
if (!draggingRef.current || !wall || !selectedWallAsset) return;
if (controls) {
(controls as any).enabled = false;
}
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?.object.userData.wallUuid) {
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();
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 && !toggleView && activeModule === 'builder') {
canvasElement.addEventListener('mousemove', onPointerMove);
canvasElement.addEventListener('pointerup', onPointerUp);
}
return () => {
canvasElement.removeEventListener('mousemove', onPointerMove);
canvasElement.removeEventListener('pointerup', onPointerUp);
};
}, [gl, camera, toggleView, activeModule, selectedWallAsset, socket, wall, wallAsset]);
const handlePointerEnter = useCallback((evt: ThreeEvent<MouseEvent>) => {
if (!toggleView) {
evt.stopPropagation();
let currentObject = evt.object;
while (currentObject) {
if (currentObject.userData.wallUuid) {
break;
}
currentObject = currentObject.parent as THREE.Object3D;
}
if (activeTool === "delete" && activeModule === 'builder') {
if (deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) {
return;
} else {
setDeletableWallAsset(currentObject);
}
}
}
}, [activeTool, activeModule, deletableWallAsset, toggleView]);
const handlePointerLeave = useCallback((evt: ThreeEvent<MouseEvent>) => {
if (!toggleView) {
evt.stopPropagation();
if (evt.intersections.length === 0 && activeTool === "delete" && deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) {
setDeletableWallAsset(null);
}
}
}, [activeTool, deletableWallAsset, toggleView]);
const handlePointerDown = useCallback((e: ThreeEvent<MouseEvent>) => {
if (!toggleView && activeModule === 'builder' && selectedWallAsset && selectedWallAsset.userData.modelUuid === wallAsset.modelUuid) {
draggingRef.current = true;
e.stopPropagation();
setSelectedWallAsset(groupRef.current);
if (controls) {
(controls as any).enabled = false;
}
}
}, [toggleView, activeModule, selectedWallAsset, wallAsset]);
const handleClick = useCallback((e: ThreeEvent<MouseEvent>) => {
if (!toggleView && activeModule === 'builder' && activeTool !== 'delete') {
if (e.object) {
e.stopPropagation();
let currentObject = e.object;
while (currentObject) {
if (currentObject.userData.wallUuid) {
break;
}
currentObject = currentObject.parent as THREE.Object3D;
}
setSelectedWallAsset(currentObject);
}
} else if (!toggleView && activeModule === 'builder' && activeTool === 'delete') {
if (activeTool === 'delete' && deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) {
const removedWallAsset = removeWallAsset(wallAsset.modelUuid);
if (projectId && removedWallAsset) {
if (!socket?.connected) {
// API
deleteWallAssetApi(projectId, selectedVersion?.versionId || '', removedWallAsset.modelUuid, removedWallAsset.wallUuid);
} else {
// SOCKET
const data = {
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization,
modelUuid: removedWallAsset.modelUuid,
wallUuid: removedWallAsset.wallUuid
}
socket.emit('v1:wall-asset:delete', data);
}
}
}
}
}, [toggleView, activeModule, activeTool, wallAsset, deletableWallAsset, socket]);
const handlePointerMissed = useCallback(() => {
if (selectedWallAsset && selectedWallAsset.userData.modelUuid === wallAsset.modelUuid) {
setSelectedWallAsset(null);
}
}, [selectedWallAsset, wallAsset]);
return {
handlePointerDown,
handleClick,
handlePointerMissed,
handlePointerEnter,
handlePointerLeave,
draggingRef
};
}

View File

@@ -1,42 +1,22 @@
import * as THREE from 'three';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ThreeEvent, useThree } from '@react-three/fiber';
import { useEffect, useMemo, useRef, useState } from 'react';
import { Base, Geometry, Subtraction } from '@react-three/csg';
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 useModuleStore from '../../../../../store/useModuleStore';
import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils';
import { useSceneContext } from '../../../../scene/sceneContext';
import { useBuilderStore } from '../../../../../store/builder/useBuilderStore';
import { useActiveTool, useSocketStore, useToggleView } from '../../../../../store/builder/store';
import { useParams } from 'react-router-dom';
import { useVersionContext } from '../../../version/versionContext';
import { getUserData } from '../../../../../functions/getUserData';
import closestPointOnLineSegment from '../../../line/helpers/getClosestPointOnLineSegment';
import { upsertWallAssetApi } from '../../../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi';
import { deleteWallAssetApi } from '../../../../../services/factoryBuilder/asset/wallAsset/deleteWallAssetApi';
import { useWallAssetEventHandler } from './eventHandler/useWallAssetEventHandler';
function WallAssetInstance({ wallAsset }: { readonly wallAsset: WallAsset }) {
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
const { socket } = useSocketStore();
const { raycaster, pointer, camera, scene, controls, gl } = useThree();
const { wallStore, wallAssetStore } = useSceneContext();
const { wallStore } = useSceneContext();
const { walls, getWallById } = wallStore();
const { updateWallAsset, removeWallAsset, getWallAssetById } = wallAssetStore();
const { toggleView } = useToggleView();
const { activeTool } = useActiveTool();
const { activeModule } = useModuleStore();
const { selectedWallAsset, setSelectedWallAsset, setDeletableWallAsset, deletableWallAsset } = useBuilderStore();
const [gltfScene, setGltfScene] = useState<GLTF["scene"] | null>(null);
const [boundingBox, setBoundingBox] = useState<THREE.Box3 | null>(null);
const groupRef = useRef<THREE.Group>(null);
const wall = useMemo(() => getWallById(wallAsset.wallUuid), [getWallById, wallAsset.wallUuid, walls]);
const draggingRef = useRef(false);
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { userId, organization } = getUserData();
const { projectId } = useParams();
const { handlePointerDown, handleClick, handlePointerMissed, handlePointerEnter, handlePointerLeave } = useWallAssetEventHandler({ wallAsset, groupRef, wall });
useEffect(() => {
const loader = new GLTFLoader();
@@ -112,130 +92,6 @@ function WallAssetInstance({ wallAsset }: { readonly wallAsset: WallAsset }) {
}, []);
useEffect(() => {
const canvasElement = gl.domElement;
const onPointerUp = (e: PointerEvent) => {
if (draggingRef.current) {
draggingRef.current = false;
if (controls) {
(controls as any).enabled = true;
}
if (selectedWallAsset) {
const updatedWallAsset = getWallAssetById(wallAsset.modelUuid);
if (projectId && updatedWallAsset) {
if (!socket?.connected) {
// API
upsertWallAssetApi(projectId, selectedVersion?.versionId || '', updatedWallAsset);
} else {
// SOCKET
const data = {
wallAssetData: updatedWallAsset,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:wall-asset:add', data);
}
}
}
}
};
const onPointerMove = (e: any) => {
if (!draggingRef.current || !wall || !selectedWallAsset) return;
if (controls) {
(controls as any).enabled = false;
}
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?.object.userData.wallUuid) {
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();
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 && !toggleView && activeModule === 'builder') {
canvasElement.addEventListener('mousemove', onPointerMove);
canvasElement.addEventListener('pointerup', onPointerUp);
}
return () => {
canvasElement.removeEventListener('mousemove', onPointerMove);
canvasElement.removeEventListener('pointerup', onPointerUp);
};
}, [gl, camera, toggleView, activeModule, selectedWallAsset, socket])
const handlePointerClick = useCallback((wallAsset: WallAsset) => {
if (activeTool === 'delete' && deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) {
const removedWallAsset = removeWallAsset(wallAsset.modelUuid);
if (projectId && removedWallAsset) {
if (!socket?.connected) {
// API
deleteWallAssetApi(projectId, selectedVersion?.versionId || '', removedWallAsset.modelUuid, removedWallAsset.wallUuid);
} else {
// SOCKET
const data = {
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization,
modelUuid: removedWallAsset.modelUuid,
wallUuid: removedWallAsset.wallUuid
}
socket.emit('v1:wall-asset:delete', data);
}
}
}
}, [activeTool, activeModule, deletableWallAsset, socket])
const handlePointerOver = useCallback((wallAsset: WallAsset, currentObject: THREE.Object3D) => {
if (activeTool === "delete" && activeModule === 'builder') {
if (deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) {
return;
} else {
setDeletableWallAsset(currentObject);
}
}
}, [activeTool, activeModule, deletableWallAsset]);
const handlePointerOut = useCallback((evt: ThreeEvent<MouseEvent>, wallAsset: WallAsset) => {
if (evt.intersections.length === 0 && activeTool === "delete" && deletableWallAsset && deletableWallAsset.userData.modelUuid === wallAsset.modelUuid) {
setDeletableWallAsset(null);
}
}, [activeTool, deletableWallAsset]);
if (!gltfScene || !boundingBox) { return null }
const size = new THREE.Vector3();
boundingBox.getSize(size);
@@ -259,57 +115,11 @@ function WallAssetInstance({ wallAsset }: { readonly wallAsset: WallAsset }) {
</Subtraction>
{gltfScene && (
<mesh
onPointerDown={(e) => {
if (!toggleView && activeModule === 'builder' && selectedWallAsset && selectedWallAsset.userData.modelUuid === wallAsset.modelUuid) {
draggingRef.current = true;
e.stopPropagation();
setSelectedWallAsset(groupRef.current);
if (controls) {
(controls as any).enabled = false;
}
}
}}
onClick={(e) => {
if (!toggleView && activeModule === 'builder' && activeTool !== 'delete') {
if (e.object) {
e.stopPropagation();
let currentObject = e.object as THREE.Object3D;
while (currentObject) {
if (currentObject.userData.wallUuid) {
break;
}
currentObject = currentObject.parent as THREE.Object3D;
}
setSelectedWallAsset(currentObject);
}
} else if (!toggleView && activeModule === 'builder' && activeTool === 'delete') {
handlePointerClick(wallAsset);
}
}}
onPointerMissed={() => {
if (selectedWallAsset && selectedWallAsset.userData.modelUuid === wallAsset.modelUuid) {
setSelectedWallAsset(null);
}
}}
onPointerEnter={(e) => {
if (!toggleView) {
e.stopPropagation();
let currentObject = e.object as THREE.Object3D;
while (currentObject) {
if (currentObject.userData.wallUuid) {
break;
}
currentObject = currentObject.parent as THREE.Object3D;
}
handlePointerOver(wallAsset, currentObject);
}
}}
onPointerLeave={(e) => {
if (!toggleView) {
e.stopPropagation();
handlePointerOut(e, wallAsset);
}
}}
onPointerDown={handlePointerDown}
onClick={handleClick}
onPointerMissed={handlePointerMissed}
onPointerEnter={handlePointerEnter}
onPointerLeave={handlePointerLeave}
userData={wallAsset}
>
<primitive userData={wallAsset} object={gltfScene} />