Refactor floor management components and APIs: enhance FloorInstance for shape creation, update FloorInstances to handle floor rendering and UUID tracking, and improve FloorCreator with project and version context. Modify floor store methods for better floor management and integrate API calls for floor upsert and deletion. Adjust wall and point components to support new floor functionalities.

This commit is contained in:
2025-06-27 10:48:48 +05:30
parent 5003dc3504
commit 04c302ea4c
10 changed files with 287 additions and 53 deletions

View File

@@ -1,10 +1,33 @@
import React from 'react'
import { useMemo } from 'react'
import { Shape, Vector2, DoubleSide } from 'three';
import { useLoader } from '@react-three/fiber';
import { Extrude } from '@react-three/drei';
import * as Constants from '../../../../../types/world/worldConstants';
function FloorInstance({ floor }: { floor: Floor }) {
const shape = useMemo(() => {
const shape = new Shape();
const points = floor.points.map(p => new Vector2(p.position[0], p.position[2]));
if (points.length < 3) return null;
shape.moveTo(points[0].x, points[0].y);
points.forEach((pt) => { shape.lineTo(pt.x, pt.y); });
return shape;
}, [floor]);
if (!shape) return null;
return (
<>
</>
)
<group name="Floor" rotation={[Math.PI / 2, 0, 0]}>
<Extrude
args={[shape, { depth: floor.floorDepth, bevelEnabled: floor.isBeveled, bevelThickness: floor.bevelStrength }]}
position={[0, 0, 0]}
receiveShadow
>
<meshStandardMaterial color={Constants.floorConfig.defaultColor} side={DoubleSide} />
</Extrude>
</group>
);
}
export default FloorInstance

View File

@@ -34,6 +34,7 @@ function FloorInstances() {
const allLines = useMemo(() => {
const lines: { start: Point; end: Point; key: string }[] = [];
const seenUuids = new Set<string>();
floors.forEach((floor) => {
const points = floor.points;
@@ -42,11 +43,13 @@ function FloorInstances() {
for (let i = 0; i < points.length; i++) {
const current = points[i];
const next = points[(i + 1) % points.length];
if (current.pointUuid !== next.pointUuid) {
const lineKey = `${current.pointUuid}-${next.pointUuid}`;
if (current.pointUuid !== next.pointUuid && !seenUuids.has(lineKey)) {
seenUuids.add(lineKey);
lines.push({
start: current,
end: next,
key: `${current.pointUuid}-${next.pointUuid}`
key: lineKey
});
}
}
@@ -58,7 +61,7 @@ function FloorInstances() {
return (
<>
{!toggleView && floors.length > 1 && (
{!toggleView && floors.length > 0 && (
<mesh name='Floors-Group'>
{floors.map((floor) => (
<FloorInstance key={floor.floorUuid} floor={floor} />

View File

@@ -4,9 +4,14 @@ import { useThree } from '@react-three/fiber';
import { useActiveLayer, useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
import { useSceneContext } from '../../../scene/sceneContext';
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
import { useParams } from 'react-router-dom';
import { useVersionContext } from '../../version/versionContext';
import { getUserData } from '../../../../functions/getUserData';
import ReferencePoint from '../../point/reference/referencePoint';
import ReferenceFloor from './referenceFloor';
// import { upsertFloorApi } from '../../../../services/factoryBuilder/floor/upsertFloorApi';
function FloorCreator() {
const { scene, camera, raycaster, gl, pointer } = useThree();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
@@ -15,9 +20,13 @@ function FloorCreator() {
const { activeLayer } = useActiveLayer();
const { socket } = useSocketStore();
const { floorStore } = useSceneContext();
const { addFloor, removeDecal, getFloorPointById, getFloorByPoints } = floorStore();
const { addFloor, getFloorPointById, getFloorByPoints } = floorStore();
const drag = useRef(false);
const isLeftMouseDown = useRef(false);
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { userId, organization } = getUserData();
const { projectId } = useParams();
const [tempPoints, setTempPoints] = useState<Point[]>([]);
const [isCreating, setIsCreating] = useState(false);
@@ -94,14 +103,33 @@ function FloorCreator() {
};
addFloor(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);
}
setTempPoints([]);
setIsCreating(false);
} else if (tempPoints.length === 0) {
} else if (tempPoints.length === 0 || (tempPoints.length > 1 && pointIntersects.object.uuid !== tempPoints[tempPoints.length - 2].pointUuid)) {
tempPoints.push(pointIntersects.object.userData as Point);
setIsCreating(true);
} else {
setTempPoints([]);
setIsCreating(false);
tempPoints.push(pointIntersects.object.userData as Point);
setIsCreating(true);
}
} else {
setTempPoints(prev => [...prev, newPoint]);
@@ -126,6 +154,25 @@ function FloorCreator() {
};
addFloor(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);
}
}
setTempPoints([]);
setIsCreating(false);
@@ -155,7 +202,7 @@ function FloorCreator() {
canvasElement.removeEventListener("click", onMouseClick);
canvasElement.removeEventListener("contextmenu", onContext);
};
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addFloor, removeDecal, getFloorPointById, getFloorByPoints, floorDepth, isBeveled, bevelStrength, sideMaterial, topMaterial, snappedPosition, snappedPoint]);
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addFloor, getFloorPointById, getFloorByPoints, floorDepth, isBeveled, bevelStrength, sideMaterial, topMaterial, snappedPosition, snappedPoint]);
return (
<>

View File

@@ -1,14 +1,23 @@
import { useEffect } from 'react';
import { useToggleView } from '../../../store/builder/store'
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
import { useVersionContext } from '../version/versionContext';
import { useSceneContext } from '../../scene/sceneContext';
import { useParams } from 'react-router-dom';
import useModuleStore from '../../../store/useModuleStore';
import FloorCreator from './floorCreator/floorCreator';
import FloorInstances from './Instances/floorInstances';
import useModuleStore from '../../../store/useModuleStore';
import { getFloorsApi } from '../../../services/factoryBuilder/floor/getFloorsApi';
function FloorGroup() {
const { togglView } = useToggleView();
const { setSelectedFloor, setSelectedDecal } = useBuilderStore();
const { activeModule } = useModuleStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { floorStore } = useSceneContext();
const { setFloors } = floorStore();
const { projectId } = useParams();
useEffect(() => {
if (togglView || activeModule !== 'builder') {
@@ -17,6 +26,21 @@ function FloorGroup() {
}
}, [togglView, activeModule])
useEffect(() => {
if (projectId && selectedVersion) {
getFloorsApi(projectId, selectedVersion?.versionId || '').then((floors) => {
console.log('floors: ', floors);
if (floors && floors.length > 0) {
setFloors(floors);
} else {
setFloors([]);
}
}).catch((err) => {
console.log(err);
})
}
}, [projectId, selectedVersion?.versionId])
return (
<>

View File

@@ -6,12 +6,15 @@ 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';
// 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';
interface LineProps {
points: [Point, Point];
}
@@ -25,7 +28,7 @@ function Line({ points }: Readonly<LineProps>) {
const { toolMode } = useToolMode();
const { wallStore, floorStore } = useSceneContext();
const { removeWallByPoints, setPosition: setWallPosition, getWallsByPointId } = wallStore();
const { removeFloorByPoints, setPosition: setFloorPosition } = floorStore();
const { removeFloorByPoints, setPosition: setFloorPosition, getFloorsByPointId } = floorStore();
const { userId, organization } = getUserData();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
@@ -112,7 +115,52 @@ function Line({ points }: Readonly<LineProps>) {
setHoveredLine(null);
}
if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') {
removeFloorByPoints(points);
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);
}
gl.domElement.style.cursor = 'default';
@@ -197,7 +245,32 @@ function Line({ points }: Readonly<LineProps>) {
})
}
} else if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') {
// Handle floor update logic here if needed
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);
})
}
}
}

View File

@@ -14,7 +14,10 @@ import { upsertAisleApi } from '../../../services/factoryBuilder/aisle/upsertAis
import { deleteAisleApi } from '../../../services/factoryBuilder/aisle/deleteAisleApi';
// import { upsertWallApi } from '../../../services/factoryBuilder/wall/upsertWallApi';
// import { deleteWallApi } from '../../../services/factoryBuilder/wall/deleteWallApi';
import { upsertFloorApi } from '../../../services/factoryBuilder/floor/upsertFloorApi';
import { getUserData } from '../../../functions/getUserData';
import { deleteFloorApi } from '../../../services/factoryBuilder/floor/deleteFloorApi';
function Point({ point }: { readonly point: Point }) {
const materialRef = useRef<THREE.ShaderMaterial>(null);
@@ -27,7 +30,7 @@ function Point({ point }: { readonly point: Point }) {
const { aisleStore, wallStore, floorStore } = useSceneContext();
const { setPosition: setAislePosition, removePoint: removeAislePoint, getAislesByPointId } = aisleStore();
const { setPosition: setWallPosition, removePoint: removeWallPoint, getWallsByPointId } = wallStore();
const { setPosition: setFloorPosition, removePoint: removeFloorPoint } = floorStore();
const { setPosition: setFloorPosition, removePoint: removeFloorPoint, getFloorsByPointId } = floorStore();
const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle, snapFloorPoint, snapFloorAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position });
const { hoveredPoint, setHoveredPoint } = useBuilderStore();
const { userId, organization } = getUserData();
@@ -174,9 +177,28 @@ function Point({ point }: { readonly point: Point }) {
socket.emit('v1:model-Wall:add', data);
});
}
// console.log('Wall after drag: ', point);
} else if (point.pointType === 'Floor') {
// console.log('Floor after drag: ', point);
const updatedFloors = getFloorsByPointId(point.pointUuid);
if (updatedFloors && updatedFloors.length > 0 && projectId) {
updatedFloors.forEach((updatedFloor) => {
// API
// upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor);
// SOCKET
const data = {
floorData: updatedFloor,
projectId: projectId,
versionId: selectedVersion?.versionId || '',
userId: userId,
organization: organization
}
socket.emit('v1:model-Floor:add', data);
});
}
}
}
@@ -220,9 +242,51 @@ function Point({ point }: { readonly point: Point }) {
}
}
if (point.pointType === 'Floor') {
const removedFloors = removeFloorPoint(point.pointUuid);
const { removedFloors, updatedFloors } = removeFloorPoint(point.pointUuid);
setHoveredPoint(null);
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);
}
});
}
}
gl.domElement.style.cursor = 'default';

View File

@@ -1,12 +1,13 @@
import { useEffect } from 'react';
import { useToggleView } from '../../../store/builder/store';
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
import { useVersionContext } from '../version/versionContext';
import { useSceneContext } from '../../scene/sceneContext';
import { useParams } from 'react-router-dom';
import useModuleStore from '../../../store/useModuleStore';
import WallCreator from './wallCreator/wallCreator';
import WallInstances from './Instances/wallInstances';
import useModuleStore from '../../../store/useModuleStore';
import { useVersionContext } from '../version/versionContext';
import { useParams } from 'react-router-dom';
import { useSceneContext } from '../../scene/sceneContext';
import { getWallsApi } from '../../../services/factoryBuilder/wall/getWallsApi';
function WallGroup() {

View File

@@ -3,7 +3,7 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_UR
export const upsertFloorApi = async (
projectId: string,
versionId: string,
floorData: Wall
floorData: Floor
) => {
try {
const response = await fetch(`${url_Backend_dwinzo}/api/V1/UpsertFloor`, {

View File

@@ -97,7 +97,7 @@ export const useBuilderStore = create<BuilderState>()(
insideMaterial: 'Material 1',
selectedFloor: null,
floorDepth: 0.3,
floorDepth: 0.1,
isBeveled: false,
bevelStrength: 0.05,
sideMaterial: 'Default Side',

View File

@@ -7,8 +7,8 @@ interface FloorStore {
addFloor: (floor: Floor) => void;
updateFloor: (uuid: string, updated: Partial<Floor>) => void;
removeFloor: (uuid: string) => void;
removePoint: (pointUuid: string) => Floor[];
removeFloorByPoints: (Points: [Point, Point]) => Floor[];
removePoint: (pointUuid: string) => { removedFloors: Floor[], updatedFloors: Floor[] };
removeFloorByPoints: (Points: [Point, Point]) => { removedFloors: Floor[], updatedFloors: Floor[] };
clearFloors: () => void;
setPosition: (
pointUuid: string,
@@ -26,7 +26,7 @@ interface FloorStore {
updateDecalScale: (decalUuid: string, scale: number) => void;
getFloorById: (uuid: string) => Floor | undefined;
getFloorsByPointId: (uuid: string) => Floor | undefined;
getFloorsByPointId: (uuid: string) => Floor[] | [];
getFloorByPoints: (points: Point[]) => Floor | undefined;
getFloorPointById: (uuid: string) => Point | undefined;
getConnectedPoints: (uuid: string) => Point[];
@@ -58,47 +58,46 @@ export const createFloorStore = () => {
removePoint: (pointUuid) => {
const removedFloors: Floor[] = [];
const updatedFloors: Floor[] = [];
set(state => {
const updatedFloors: Floor[] = [];
for (const floor of state.floors) {
const hasPoint = floor.points.some(p => p.pointUuid === pointUuid);
if (!hasPoint) {
updatedFloors.push(floor);
const pointIndex = floor.points.findIndex(p => p.pointUuid === pointUuid);
if (pointIndex === -1) {
updatedFloors.push(JSON.parse(JSON.stringify(floor)));
continue;
}
const remainingPoints = floor.points.filter(p => p.pointUuid !== pointUuid);
if (remainingPoints.length > 2) {
floor.points = remainingPoints;
updatedFloors.push(floor);
} else {
removedFloors.push(floor);
if (remainingPoints.length <= 2) {
removedFloors.push(JSON.parse(JSON.stringify(floor)));
continue;
}
floor.points = remainingPoints;
updatedFloors.push(JSON.parse(JSON.stringify(floor)));
}
state.floors = updatedFloors;
});
return removedFloors;
return { removedFloors, updatedFloors };
},
removeFloorByPoints: ([pointA, pointB]) => {
const removedFloors: Floor[] = [];
const updatedFloors: Floor[] = [];
set(state => {
const updatedFloors: Floor[] = [];
for (const floor of state.floors) {
const indices = floor.points
.map((p, i) => ({ uuid: p.pointUuid, index: i }));
const indices = floor.points.map((p, i) => ({ uuid: p.pointUuid, index: i }));
const idxA = indices.find(i => i.uuid === pointA.pointUuid)?.index ?? -1;
const idxB = indices.find(i => i.uuid === pointB.pointUuid)?.index ?? -1;
if (idxA === -1 || idxB === -1) {
updatedFloors.push(floor);
updatedFloors.push(JSON.parse(JSON.stringify(floor)));
continue;
}
@@ -108,7 +107,7 @@ export const createFloorStore = () => {
(idxB === 0 && idxA === floor.points.length - 1);
if (!areAdjacent) {
updatedFloors.push(floor);
updatedFloors.push(JSON.parse(JSON.stringify(floor)));
continue;
}
@@ -118,16 +117,16 @@ export const createFloorStore = () => {
if (remainingPoints.length > 2) {
floor.points = remainingPoints;
updatedFloors.push(floor);
updatedFloors.push(JSON.parse(JSON.stringify(floor)));
} else {
removedFloors.push(floor);
removedFloors.push(JSON.parse(JSON.stringify(floor)));
}
}
state.floors = updatedFloors;
});
return removedFloors;
return { removedFloors, updatedFloors };
},
clearFloors: () => set(state => {
@@ -236,9 +235,9 @@ export const createFloorStore = () => {
},
getFloorsByPointId: (pointUuid) => {
return get().floors.find(floor =>
floor.points.some(p => p.pointUuid === pointUuid)
);
return get().floors.filter(floor => {
return floor.points.some(p => p.pointUuid === pointUuid);
});
},
getFloorByPoints: (points) => {