diff --git a/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx b/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx index 1b6dcb7..6c9016e 100644 --- a/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx +++ b/app/src/modules/builder/floor/Instances/Instance/floorInstance.tsx @@ -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 ( - <> - - ) + + + + + + ); } export default FloorInstance \ No newline at end of file diff --git a/app/src/modules/builder/floor/Instances/floorInstances.tsx b/app/src/modules/builder/floor/Instances/floorInstances.tsx index 75fd359..a5969eb 100644 --- a/app/src/modules/builder/floor/Instances/floorInstances.tsx +++ b/app/src/modules/builder/floor/Instances/floorInstances.tsx @@ -34,6 +34,7 @@ function FloorInstances() { const allLines = useMemo(() => { const lines: { start: Point; end: Point; key: string }[] = []; + const seenUuids = new Set(); 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 && ( {floors.map((floor) => ( diff --git a/app/src/modules/builder/floor/floorCreator/floorCreator.tsx b/app/src/modules/builder/floor/floorCreator/floorCreator.tsx index 2d09ff9..aaeb0f3 100644 --- a/app/src/modules/builder/floor/floorCreator/floorCreator.tsx +++ b/app/src/modules/builder/floor/floorCreator/floorCreator.tsx @@ -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([]); 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 ( <> diff --git a/app/src/modules/builder/floor/floorGroup.tsx b/app/src/modules/builder/floor/floorGroup.tsx index fdcf37c..53a81f4 100644 --- a/app/src/modules/builder/floor/floorGroup.tsx +++ b/app/src/modules/builder/floor/floorGroup.tsx @@ -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 ( <> diff --git a/app/src/modules/builder/line/line.tsx b/app/src/modules/builder/line/line.tsx index 42aa5c2..f75be5d 100644 --- a/app/src/modules/builder/line/line.tsx +++ b/app/src/modules/builder/line/line.tsx @@ -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) { 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) { 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) { }) } } 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); + }) + } } } diff --git a/app/src/modules/builder/point/point.tsx b/app/src/modules/builder/point/point.tsx index 504617d..fe2dd6e 100644 --- a/app/src/modules/builder/point/point.tsx +++ b/app/src/modules/builder/point/point.tsx @@ -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(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'; diff --git a/app/src/modules/builder/wall/wallGroup.tsx b/app/src/modules/builder/wall/wallGroup.tsx index 0722d56..bd8bad1 100644 --- a/app/src/modules/builder/wall/wallGroup.tsx +++ b/app/src/modules/builder/wall/wallGroup.tsx @@ -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() { diff --git a/app/src/services/factoryBuilder/floor/upsertFloorApi.ts b/app/src/services/factoryBuilder/floor/upsertFloorApi.ts index a00d8f7..45590c1 100644 --- a/app/src/services/factoryBuilder/floor/upsertFloorApi.ts +++ b/app/src/services/factoryBuilder/floor/upsertFloorApi.ts @@ -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`, { diff --git a/app/src/store/builder/useBuilderStore.ts b/app/src/store/builder/useBuilderStore.ts index 18facd9..e35db75 100644 --- a/app/src/store/builder/useBuilderStore.ts +++ b/app/src/store/builder/useBuilderStore.ts @@ -97,7 +97,7 @@ export const useBuilderStore = create()( insideMaterial: 'Material 1', selectedFloor: null, - floorDepth: 0.3, + floorDepth: 0.1, isBeveled: false, bevelStrength: 0.05, sideMaterial: 'Default Side', diff --git a/app/src/store/builder/useFloorStore.ts b/app/src/store/builder/useFloorStore.ts index c3ddf7d..7d425eb 100644 --- a/app/src/store/builder/useFloorStore.ts +++ b/app/src/store/builder/useFloorStore.ts @@ -7,8 +7,8 @@ interface FloorStore { addFloor: (floor: Floor) => void; updateFloor: (uuid: string, updated: Partial) => 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) => {