Files
Dwinzo_Demo/app/src/modules/builder/line/line.tsx

398 lines
16 KiB
TypeScript
Raw Normal View History

2025-06-10 15:28:23 +05:30
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 { useVersionContext } from '../version/versionContext';
import { useParams } from 'react-router-dom';
import { getUserData } from '../../../functions/getUserData';
2025-06-10 15:28:23 +05:30
// import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi';
// 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';
2025-06-10 15:28:23 +05:30
interface LineProps {
points: [Point, Point];
}
function Line({ points }: Readonly<LineProps>) {
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, 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();
const { projectId } = useParams();
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
const { hoveredLine, setHoveredLine, hoveredPoint } = useBuilderStore();
2025-06-10 15:28:23 +05:30
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') {
const { removedFloors, updatedFloors } = removeFloorByPoints(points);
if (removedFloors.length > 0) {
removedFloors.forEach(floor => {
if (projectId) {
// API
// deleteFloorApi(projectId, selectedVersion?.versionId || '', floor.floorUuid);
// SOCKET
const data = {
floorUuid: floor.floorUuid,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:delete', data);
}
});
}
if (updatedFloors.length > 0) {
updatedFloors.forEach(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);
}
});
}
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';
}
}
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]);
}
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]);
}
}
}
};
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') {
const updatedFloors1 = getFloorsByPointId(points[0].pointUuid);
const updatedFloors2 = getFloorsByPointId(points[1].pointUuid);
const updatedFloors = [...updatedFloors1, ...updatedFloors2].filter((floor, index, self) => index === self.findIndex((f) => f.floorUuid === floor.floorUuid));
if (updatedFloors.length > 0 && projectId) {
updatedFloors.forEach(updatedFloor => {
// API
// upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor).catch((error) => {
// console.error('Error updating floor:', error);
// });
// SOCKET
const data = {
floorData: updatedFloor,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
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);
})
}
}
}
2025-06-10 15:28:23 +05:30
return (
<DragControls
axisLock="y"
autoTransform={false}
onDragStart={() => handleDragStart(points)}
onDrag={() => handleDrag(points)}
onDragEnd={() => handleDragEnd(points)}
2025-06-10 15:28:23 +05:30
>
<Tube
name={`${points[0].pointType}-Line`}
key={`${points[0].pointUuid}-${points[1].pointUuid}`}
uuid={`${points[0].pointUuid}-${points[1].pointUuid}`}
userData={{ points, path }}
args={[path, Constants.lineConfig.tubularSegments, Constants.lineConfig.radius, Constants.lineConfig.radialSegments, false]}
onClick={() => {
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)
}}
>
<meshStandardMaterial color={isDeletable ? colors.defaultDeleteColor : colors.defaultLineColor} />
</Tube>
</DragControls >
2025-06-10 15:28:23 +05:30
);
}
export default Line;