diff --git a/app/src/modules/builder/line/line.tsx b/app/src/modules/builder/line/line.tsx index 474eb9e..fd52915 100644 --- a/app/src/modules/builder/line/line.tsx +++ b/app/src/modules/builder/line/line.tsx @@ -32,7 +32,7 @@ function Line({ points }: Readonly) { const { toolMode } = useToolMode(); const { wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext(); const { push2D } = undoRedo2DStore(); - const { removeWallByPoints, setPosition: setWallPosition, getWallsByPointId } = wallStore(); + const { removeWallByPoints, setPosition: setWallPosition, getWallByPoints, getConnectedWallsByWallId } = wallStore(); const { removeFloorByPoints, setPosition: setFloorPosition, getFloorsByPointId, getFloorsByPoints } = floorStore(); const { removeZoneByPoints, setPosition: setZonePosition, getZonesByPointId, getZonesByPoints } = zoneStore(); const { userId, organization } = getUserData(); @@ -361,27 +361,31 @@ function Line({ points }: Readonly) { const offset = new THREE.Vector3().subVectors(midPoint, hit); setDragOffset(offset); - if (points[0].pointType === 'Wall') { - const walls = getWallsByPointId(points[0].pointUuid); - setInitialPositions({ walls }); - } else if (points[0].pointType === 'Floor') { + if (points[0].pointType === 'Wall' && points[1].pointType === 'Wall') { + const wall = getWallByPoints(points); + if (wall) { + const walls = getConnectedWallsByWallId(wall.wallUuid, false); + setInitialPositions({ walls }); + } + } else if (points[0].pointType === 'Floor' && points[0].pointType === 'Floor') { const floors = getFloorsByPointId(points[0].pointUuid); setInitialPositions({ floors }); - } else if (points[0].pointType === 'Zone') { + } else if (points[0].pointType === 'Zone' && points[0].pointType === 'Zone') { const zones = getZonesByPointId(points[0].pointUuid); setInitialPositions({ zones }); } } }; - const handleDragEnd = (points: [Point, Point]) => { + const handleDragEnd = (points: [Point, Point]) => { if (toolMode !== 'move' || !dragOffset) return; handleCanvasCursors('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)); + const wall = getWallByPoints(points); + if (!wall) return; + const updatedWalls = getConnectedWallsByWallId(wall.wallUuid, false); if (updatedWalls.length > 0 && projectId) { updatedWalls.forEach(updatedWall => { diff --git a/app/src/modules/builder/point/point.tsx b/app/src/modules/builder/point/point.tsx index cf44c26..ca11e91 100644 --- a/app/src/modules/builder/point/point.tsx +++ b/app/src/modules/builder/point/point.tsx @@ -19,9 +19,12 @@ import { useSceneContext } from '../../scene/sceneContext'; // import { deleteFloorApi } from '../../../services/factoryBuilder/floor/deleteFloorApi'; // import { upsertZoneApi } from '../../../services/factoryBuilder/zone/upsertZoneApi'; // import { deleteZoneApi } from '../../../services/factoryBuilder/zone/deleteZoneApi'; +// import { upsertWallAssetApi } from '../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi'; +// import { deleteWallAssetApi } from '../../../services/factoryBuilder/asset/wallAsset/deleteWallAssetApi'; import { getUserData } from '../../../functions/getUserData'; import { handleCanvasCursors } from '../../../utils/mouseUtils/handleCanvasCursors'; +import { calculateAssetTransformationOnWall } from '../wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall'; function Point({ point }: { readonly point: Point }) { const materialRef = useRef(null); @@ -32,12 +35,13 @@ function Point({ point }: { readonly point: Point }) { const [dragOffset, setDragOffset] = useState(null); const { socket } = useSocketStore(); const { toolMode } = useToolMode(); - const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext(); + const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore, wallAssetStore } = useSceneContext(); const { push2D } = undoRedo2DStore(); const { setPosition: setAislePosition, removePoint: removeAislePoint, getAislesByPointId } = aisleStore(); const { setPosition: setWallPosition, removePoint: removeWallPoint, getWallsByPointId } = wallStore(); const { setPosition: setFloorPosition, removePoint: removeFloorPoint, getFloorsByPointId } = floorStore(); const { setPosition: setZonePosition, removePoint: removeZonePoint, getZonesByPointId } = zoneStore(); + const { getWallAssetsByWall, updateWallAsset, removeWallAsset } = wallAssetStore(); const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle, snapFloorPoint, snapFloorAngle, snapZonePoint, snapZoneAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position }); const { hoveredPoint, hoveredLine, setHoveredPoint } = useBuilderStore(); const { selectedPoints } = useSelectedPoints(); @@ -226,6 +230,39 @@ function Point({ point }: { readonly point: Point }) { if (updatedWalls && updatedWalls.length > 0 && projectId) { updatedWalls.forEach((updatedWall) => { + const initialWall = initialPositions.walls?.find(w => w.wallUuid === updatedWall.wallUuid); + + if (initialWall) { + const assetsOnWall = getWallAssetsByWall(updatedWall.wallUuid); + + assetsOnWall.forEach(asset => { + const { position, rotation } = calculateAssetTransformationOnWall(asset, initialWall, updatedWall); + + const updatedWallAsset = updateWallAsset(asset.modelUuid, { + position: [position[0], asset.position[1], position[2]], + rotation: rotation + }); + + if (projectId && updatedWallAsset) { + // API + + // upsertWallAssetApi(projectId, selectedVersion?.versionId || '', updatedWallAsset); + + // SOCKET + + const data = { + wallAssetData: updatedWallAsset, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:wall-asset:add', data); + } + }); + } + // API // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall); @@ -393,6 +430,33 @@ function Point({ point }: { readonly point: Point }) { if (removedWalls.length > 0) { setHoveredPoint(null); removedWalls.forEach(wall => { + const assetsOnWall = getWallAssetsByWall(wall.wallUuid); + + assetsOnWall.forEach((asset) => { + if (projectId && asset) { + + removeWallAsset(asset.modelUuid); + + // API + + // deleteWallAssetApi(projectId, selectedVersion?.versionId || '', asset.modelUuid, asset.wallUuid); + + // SOCKET + + const data = { + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization, + modelUuid: asset.modelUuid, + wallUuid: asset.wallUuid + } + + socket.emit('v1:wall-asset:delete', data); + + } + }) + if (projectId) { // API diff --git a/app/src/modules/builder/wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall.ts b/app/src/modules/builder/wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall.ts new file mode 100644 index 0000000..aa881c8 --- /dev/null +++ b/app/src/modules/builder/wallAsset/Instances/Instance/functions/calculateAssetTransformationOnWall.ts @@ -0,0 +1,46 @@ +import * as THREE from 'three'; + +const calculateAssetTransformationOnWall = ( + asset: WallAsset, + initialWall: Wall, + newWall: Wall +): { position: [number, number, number], rotation: [number, number, number] } => { + const [initialStartPoint, initialEndPoint] = initialWall.points; + const [newStartPoint, newEndPoint] = newWall.points; + + const initialWallVector = new THREE.Vector3(initialEndPoint.position[0] - initialStartPoint.position[0], 0, initialEndPoint.position[2] - initialStartPoint.position[2]); + + const assetVector = new THREE.Vector3(asset.position[0] - initialStartPoint.position[0], 0, asset.position[2] - initialStartPoint.position[2]); + + const initialWallLength = initialWallVector.length(); + const initialWallNormalized = initialWallVector.normalize(); + const dotProduct = assetVector.dot(initialWallNormalized); + + const projection = initialWallNormalized.clone().multiplyScalar(dotProduct); + const perpendicular = new THREE.Vector3().subVectors(assetVector, projection); + const distanceFromWall = perpendicular.length(); + + const crossProduct = new THREE.Vector3().crossVectors(initialWallNormalized, perpendicular).y; + const signedDistance = distanceFromWall * (crossProduct >= 0 ? 1 : -1); + + const percentage = Math.max(0, Math.min(1, dotProduct / initialWallLength)); + + const newWallVector = new THREE.Vector3(newEndPoint.position[0] - newStartPoint.position[0], 0, newEndPoint.position[2] - newStartPoint.position[2]); + + const x = newStartPoint.position[0] + (newEndPoint.position[0] - newStartPoint.position[0]) * percentage; + const z = newStartPoint.position[2] + (newEndPoint.position[2] - newStartPoint.position[2]) * percentage; + + const newWallNormal = new THREE.Vector3(-newWallVector.z, 0, newWallVector.x).normalize(); + + const offsetX = newWallNormal.x * signedDistance; + const offsetZ = newWallNormal.z * signedDistance; + + const wallAngle = Math.atan2(newWallVector.z, newWallVector.x); + + return { + position: [x + offsetX, asset.position[1], z + offsetZ], + rotation: [0, -wallAngle, 0] + }; +}; + +export { calculateAssetTransformationOnWall }; \ No newline at end of file diff --git a/app/src/modules/builder/wallAsset/Instances/Instances/wallAssetInstance.tsx b/app/src/modules/builder/wallAsset/Instances/Instance/wallAssetInstance.tsx similarity index 100% rename from app/src/modules/builder/wallAsset/Instances/Instances/wallAssetInstance.tsx rename to app/src/modules/builder/wallAsset/Instances/Instance/wallAssetInstance.tsx diff --git a/app/src/modules/builder/wallAsset/Instances/wallAssetInstances.tsx b/app/src/modules/builder/wallAsset/Instances/wallAssetInstances.tsx index 581dfe7..d47c263 100644 --- a/app/src/modules/builder/wallAsset/Instances/wallAssetInstances.tsx +++ b/app/src/modules/builder/wallAsset/Instances/wallAssetInstances.tsx @@ -1,7 +1,7 @@ import { useEffect } from 'react'; import { useSceneContext } from '../../../scene/sceneContext' import { useToggleView } from '../../../../store/builder/store'; -import WallAssetInstance from './Instances/wallAssetInstance'; +import WallAssetInstance from './Instance/wallAssetInstance'; function WallAssetInstances() { const { wallAssetStore } = useSceneContext(); diff --git a/app/src/modules/builder/wallAsset/wallAssetCreator.tsx b/app/src/modules/builder/wallAsset/wallAssetCreator.tsx index 5001e28..fb95236 100644 --- a/app/src/modules/builder/wallAsset/wallAssetCreator.tsx +++ b/app/src/modules/builder/wallAsset/wallAssetCreator.tsx @@ -9,7 +9,7 @@ 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 { upsertWallAssetApi } from '../../../services/factoryBuilder/asset/wallAsset/upsertWallAssetApi'; function WallAssetCreator() { const { socket } = useSocketStore(); diff --git a/app/src/modules/scene/controls/selectionControls/selection2D/moveControls2D.tsx b/app/src/modules/scene/controls/selectionControls/selection2D/moveControls2D.tsx index 2555a13..83853e9 100644 --- a/app/src/modules/scene/controls/selectionControls/selection2D/moveControls2D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection2D/moveControls2D.tsx @@ -66,7 +66,7 @@ function MoveControls2D({ const onPointerUp = (event: PointerEvent) => { if (!isMoving && movedObjects.length > 0 && event.button === 0) { event.preventDefault(); - placeMovedAssets(); + placeMovedPoints(); } if (!isMoving && movedObjects.length > 0 && event.button === 2) { event.preventDefault(); @@ -221,7 +221,7 @@ function MoveControls2D({ }, 0) }; - const placeMovedAssets = () => { + const placeMovedPoints = () => { if (movedObjects.length === 0) return; const undoPoints: UndoRedo2DDataTypeSchema[] = []; @@ -262,11 +262,11 @@ function MoveControls2D({ lineData: { ...updatedAisle, points: [ - updatedAisle.points[0].pointUuid === point.pointUuid - ? { ...updatedAisle.points[0], position: [old.position.x, old.position.y, old.position.z] } + initialStates[updatedAisle.points[0].pointUuid] ? + { ...updatedAisle.points[0], position: initialStates[updatedAisle.points[0].pointUuid].position } : updatedAisle.points[0], - updatedAisle.points[1].pointUuid === point.pointUuid - ? { ...updatedAisle.points[1], position: [old.position.x, old.position.y, old.position.z] } + initialStates[updatedAisle.points[1].pointUuid] ? + { ...updatedAisle.points[1], position: initialStates[updatedAisle.points[1].pointUuid].position } : updatedAisle.points[1] ] as [Point, Point], }, @@ -302,11 +302,11 @@ function MoveControls2D({ lineData: { ...updatedWall, points: [ - updatedWall.points[0].pointUuid === point.pointUuid - ? { ...updatedWall.points[0], position: [old.position.x, old.position.y, old.position.z] } + initialStates[updatedWall.points[0].pointUuid] ? + { ...updatedWall.points[0], position: initialStates[updatedWall.points[0].pointUuid].position } : updatedWall.points[0], - updatedWall.points[1].pointUuid === point.pointUuid - ? { ...updatedWall.points[1], position: [old.position.x, old.position.y, old.position.z] } + initialStates[updatedWall.points[1].pointUuid] ? + { ...updatedWall.points[1], position: initialStates[updatedWall.points[1].pointUuid].position } : updatedWall.points[1] ] as [Point, Point], }, diff --git a/app/src/store/builder/useWallStore.ts b/app/src/store/builder/useWallStore.ts index 11f7130..db40269 100644 --- a/app/src/store/builder/useWallStore.ts +++ b/app/src/store/builder/useWallStore.ts @@ -25,6 +25,7 @@ interface WallStore { getWallByPoints: (points: Point[]) => Wall | undefined; getWallPointById: (uuid: string) => Point | undefined; getConnectedPoints: (uuid: string) => Point[]; + getConnectedWallsByWallId: (wallUuid: string, skipSelf: boolean) => Wall[]; } export const createWallStore = () => { @@ -178,7 +179,7 @@ export const createWallStore = () => { getWallsByPointId: (uuid) => { return get().walls.filter((a) => { - return a.points.some((p) => p.pointUuid === uuid); + return JSON.parse(JSON.stringify(a.points.some((p) => p.pointUuid === uuid))); }) }, @@ -211,6 +212,19 @@ export const createWallStore = () => { } return connected; }, + + getConnectedWallsByWallId: (wallUuid, skipSelf) => { + const wall = get().walls.find(w => w.wallUuid === wallUuid); + if (!wall) return []; + + const pointUuids = wall.points.map(p => p.pointUuid); + + return get().walls.filter(w => { + if (skipSelf && w.wallUuid === wallUuid) return false; + return w.points.some(p => pointUuids.includes(p.pointUuid)); + }); + }, + })) ) }