diff --git a/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx b/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx index af08226..aea7cd2 100644 --- a/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx +++ b/app/src/modules/builder/aisle/aisleCreator/aisleCreator.tsx @@ -19,8 +19,9 @@ function AisleCreator() { const { toolMode } = useToolMode(); const { activeLayer } = useActiveLayer(); const { socket } = useSocketStore(); - const { aisleStore } = useSceneContext(); + const { aisleStore, undoRedo2DStore } = useSceneContext(); const { addAisle, getAislePointById } = aisleStore(); + const { push2D } = undoRedo2DStore(); const drag = useRef(false); const isLeftMouseDown = useRef(false); const { selectedVersionStore } = useVersionContext(); @@ -107,7 +108,23 @@ function AisleCreator() { aisleWidth: aisleWidth } }; + addAisle(aisle); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Line-Create', + point: { + type: 'Aisle', + lineData: aisle, + timeStamp: new Date().toISOString(), + } + } + ], + }) + if (projectId) { // API @@ -145,7 +162,23 @@ function AisleCreator() { gapLength: gapLength } }; + addAisle(aisle); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Line-Create', + point: { + type: 'Aisle', + lineData: aisle, + timeStamp: new Date().toISOString(), + } + } + ], + }) + if (projectId) { // API @@ -182,7 +215,23 @@ function AisleCreator() { gapLength: gapLength } }; + addAisle(aisle); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Line-Create', + point: { + type: 'Aisle', + lineData: aisle, + timeStamp: new Date().toISOString(), + } + } + ], + }) + if (projectId) { // API @@ -218,7 +267,23 @@ function AisleCreator() { aisleWidth: aisleWidth } }; + addAisle(aisle); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Line-Create', + point: { + type: 'Aisle', + lineData: aisle, + timeStamp: new Date().toISOString(), + } + } + ], + }) + if (projectId) { // API @@ -256,7 +321,23 @@ function AisleCreator() { gapLength: gapLength } }; + addAisle(aisle); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Line-Create', + point: { + type: 'Aisle', + lineData: aisle, + timeStamp: new Date().toISOString(), + } + } + ], + }) + if (projectId) { // API @@ -293,7 +374,23 @@ function AisleCreator() { isFlipped: isFlipped } }; + addAisle(aisle); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Line-Create', + point: { + type: 'Aisle', + lineData: aisle, + timeStamp: new Date().toISOString(), + } + } + ], + }) + if (projectId) { // API @@ -329,7 +426,23 @@ function AisleCreator() { aisleWidth: aisleWidth } }; + addAisle(aisle); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Line-Create', + point: { + type: 'Aisle', + lineData: aisle, + timeStamp: new Date().toISOString(), + } + } + ], + }) + if (projectId) { // API @@ -366,7 +479,23 @@ function AisleCreator() { isFlipped: isFlipped } }; + addAisle(aisle); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Line-Create', + point: { + type: 'Aisle', + lineData: aisle, + timeStamp: new Date().toISOString(), + } + } + ], + }) + if (projectId) { // API diff --git a/app/src/modules/builder/asset/models/model/eventHandlers/useEventHandlers.ts b/app/src/modules/builder/asset/models/model/eventHandlers/useEventHandlers.ts new file mode 100644 index 0000000..d678382 --- /dev/null +++ b/app/src/modules/builder/asset/models/model/eventHandlers/useEventHandlers.ts @@ -0,0 +1,238 @@ +import * as THREE from 'three'; +import { CameraControls } from '@react-three/drei'; +import { ThreeEvent } from '@react-three/fiber'; +import { useCallback } from 'react'; +import { ProductStoreType } from '../../../../../../store/simulation/useProductStore'; +import { EventStoreType } from '../../../../../../store/simulation/useEventsStore'; +import { Socket } from 'socket.io-client'; + +import { useActiveTool, useToolMode } from '../../../../../../store/builder/store'; +import useModuleStore, { useSubModuleStore } from '../../../../../../store/useModuleStore'; +import { useSocketStore } from '../../../../../../store/builder/store'; +import { useSceneContext } from '../../../../../scene/sceneContext'; +import { useProductContext } from '../../../../../simulation/products/productContext'; +import { useVersionContext } from '../../../../version/versionContext'; +import { useParams } from 'react-router-dom'; +import { getUserData } from '../../../../../../functions/getUserData'; + +// import { deleteFloorItem } from '../../../../../../services/factoryBuilder/asset/floorAsset/deleteFloorItemApi'; + +export function useModelEventHandlers({ + controls, + boundingBox, + groupRef, + toggleView, + deletableFloorItem, + setDeletableFloorItem, + setSelectedFloorItem, + gl, + setTop, + setLeft, + getIsEventInProduct, + getEventByModelUuid, + setSelectedAsset, + clearSelectedAsset, + removeAsset, + updateBackend, + leftDrag, + rightDrag +}: { + controls: CameraControls | any, + boundingBox: THREE.Box3 | null, + groupRef: React.RefObject, + toggleView: boolean, + deletableFloorItem: THREE.Object3D | null, + setDeletableFloorItem: (item: THREE.Object3D | null) => void, + setSelectedFloorItem: (item: THREE.Object3D | null) => void, + gl: THREE.WebGLRenderer, + setTop: (top: number) => void, + setLeft: (left: number) => void, + getIsEventInProduct: (productUuid: string, modelUuid: string) => boolean, + getEventByModelUuid: (modelUuid: string) => EventsSchema | undefined, + setSelectedAsset: (EventData: EventsSchema) => void, + clearSelectedAsset: () => void, + removeAsset: (modelUuid: string) => void, + updateBackend: (productName: string, productUuid: string, projectId: string, event: EventsSchema) => void, + leftDrag: React.MutableRefObject, + rightDrag: React.MutableRefObject +}) { + + const { activeTool } = useActiveTool(); + const { activeModule } = useModuleStore(); + const { subModule } = useSubModuleStore(); + const { socket } = useSocketStore(); + const { eventStore, productStore } = useSceneContext(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { projectId } = useParams(); + const { userId, organization } = getUserData(); + + const handleDblClick = (asset: Asset) => { + if (asset && activeTool === "cursor" && boundingBox && groupRef.current && activeModule === 'builder') { + const size = boundingBox.getSize(new THREE.Vector3()); + const center = boundingBox.getCenter(new THREE.Vector3()); + + const front = new THREE.Vector3(0, 0, 1); + groupRef.current.localToWorld(front); + front.sub(groupRef.current.position).normalize(); + + const distance = Math.max(size.x, size.y, size.z) * 2; + const newPosition = center.clone().addScaledVector(front, distance); + + (controls as CameraControls).setPosition(newPosition.x, newPosition.y, newPosition.z, true); + (controls as CameraControls).setTarget(center.x, center.y, center.z, true); + (controls as CameraControls).fitToBox(groupRef.current, true, { + cover: true, + paddingTop: 5, + paddingLeft: 5, + paddingBottom: 5, + paddingRight: 5, + }); + + setSelectedFloorItem(groupRef.current); + } + }; + + const handleClick = async (evt: ThreeEvent, asset: Asset) => { + if (leftDrag.current || toggleView) return; + if (activeTool === 'delete' && deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) { + + //REST + + // const response = await deleteFloorItem(organization, asset.modelUuid, asset.modelName); + + //SOCKET + + const data = { + organization, + modelUuid: asset.modelUuid, + modelName: asset.modelName, + socketId: socket.id, + userId, + versionId: selectedVersion?.versionId || '', + projectId + } + + const response = socket.emit('v1:model-asset:delete', data) + + eventStore.getState().removeEvent(asset.modelUuid); + const updatedEvents = productStore.getState().deleteEvent(asset.modelUuid); + + updatedEvents.forEach((event) => { + updateBackend( + selectedProduct.productName, + selectedProduct.productUuid, + projectId || '', + event + ); + }) + + if (response) { + + removeAsset(asset.modelUuid); + + echo.success("Model Removed!"); + } + + } else if (activeModule === 'simulation' && subModule === "simulations" && activeTool === 'pen') { + if (asset.eventData && asset.eventData.type === 'Conveyor') { + const intersectedPoint = evt.point; + const localPosition = groupRef.current?.worldToLocal(intersectedPoint.clone()); + if (localPosition) { + const conveyorPoint: ConveyorPointSchema = { + uuid: THREE.MathUtils.generateUUID(), + position: [localPosition?.x, localPosition?.y, localPosition?.z], + rotation: [0, 0, 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: `Action 1`, + actionType: 'default', + material: 'Default Material', + delay: 0, + spawnInterval: 5, + spawnCount: 1, + triggers: [] + } + } + + const event = productStore.getState().addPoint(selectedProduct.productUuid, asset.modelUuid, conveyorPoint); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productUuid, + projectId || '', + event + ); + } + } + } + + } + }; + + const handlePointerOver = useCallback((asset: Asset) => { + if (activeTool === "delete" && activeModule === 'builder') { + if (deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) { + return; + } else { + setDeletableFloorItem(groupRef.current); + } + } + }, [activeTool, activeModule, deletableFloorItem]); + + const handlePointerOut = useCallback((evt: ThreeEvent, asset: Asset) => { + if (evt.intersections.length === 0 && activeTool === "delete" && deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) { + setDeletableFloorItem(null); + } + }, [activeTool, deletableFloorItem]); + + const handleContextMenu = (asset: Asset, evt: ThreeEvent) => { + if (rightDrag.current || toggleView) return; + if (activeTool === "cursor" && subModule === 'simulations') { + if (asset.modelUuid) { + const canvasElement = gl.domElement; + const isInProduct = getIsEventInProduct(selectedProduct.productUuid, asset.modelUuid); + if (isInProduct) { + const event = getEventByModelUuid(asset.modelUuid); + if (event) { + setSelectedAsset(event); + const canvasRect = canvasElement.getBoundingClientRect(); + const relativeX = evt.clientX - canvasRect.left; + const relativeY = evt.clientY - canvasRect.top; + setTop(relativeY); + setLeft(relativeX); + } else { + clearSelectedAsset(); + } + } else { + const event = getEventByModelUuid(asset.modelUuid); + if (event) { + setSelectedAsset(event) + const canvasRect = canvasElement.getBoundingClientRect(); + const relativeX = evt.clientX - canvasRect.left; + const relativeY = evt.clientY - canvasRect.top; + setTop(relativeY); + setLeft(relativeX); + } else { + clearSelectedAsset() + } + } + } else { + clearSelectedAsset() + } + } else { + clearSelectedAsset() + } + } + + return { + handleDblClick, + handleClick, + handlePointerOver, + handlePointerOut, + handleContextMenu + }; +} diff --git a/app/src/modules/builder/floor/floorCreator/floorCreator.tsx b/app/src/modules/builder/floor/floorCreator/floorCreator.tsx index acab425..28450aa 100644 --- a/app/src/modules/builder/floor/floorCreator/floorCreator.tsx +++ b/app/src/modules/builder/floor/floorCreator/floorCreator.tsx @@ -19,8 +19,9 @@ function FloorCreator() { const { toolMode } = useToolMode(); const { activeLayer } = useActiveLayer(); const { socket } = useSocketStore(); - const { floorStore } = useSceneContext(); - const { addFloor, getFloorPointById, getFloorByPoints } = floorStore(); + const { floorStore, undoRedo2DStore } = useSceneContext(); + const { addFloor, getFloorPointById } = floorStore(); + const { push2D } = undoRedo2DStore(); const drag = useRef(false); const isLeftMouseDown = useRef(false); const { selectedVersionStore } = useVersionContext(); @@ -103,6 +104,21 @@ function FloorCreator() { }; addFloor(floor); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Line-Create', + point: { + type: 'Floor', + lineData: floor, + timeStamp: new Date().toISOString(), + } + } + ], + }) + if (projectId) { // API @@ -142,6 +158,21 @@ function FloorCreator() { }; addFloor(floor); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Line-Create', + point: { + type: 'Floor', + lineData: floor, + timeStamp: new Date().toISOString(), + } + } + ], + }) + if (projectId) { // API @@ -191,6 +222,21 @@ function FloorCreator() { }; addFloor(floor); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Line-Create', + point: { + type: 'Floor', + lineData: floor, + timeStamp: new Date().toISOString(), + } + } + ], + }) + if (projectId) { // API @@ -243,7 +289,7 @@ function FloorCreator() { canvasElement.removeEventListener("click", onMouseClick); canvasElement.removeEventListener("contextmenu", onContext); }; - }, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addFloor, getFloorPointById, getFloorByPoints, floorDepth, isBeveled, bevelStrength, sideMaterial, topMaterial, snappedPosition, snappedPoint]); + }, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addFloor, getFloorPointById, floorDepth, isBeveled, bevelStrength, sideMaterial, topMaterial, snappedPosition, snappedPoint]); return ( <> diff --git a/app/src/modules/builder/line/line.tsx b/app/src/modules/builder/line/line.tsx index a0470aa..474eb9e 100644 --- a/app/src/modules/builder/line/line.tsx +++ b/app/src/modules/builder/line/line.tsx @@ -30,10 +30,11 @@ function Line({ points }: Readonly) { const [isDeletable, setIsDeletable] = useState(false); const { socket } = useSocketStore(); const { toolMode } = useToolMode(); - const { wallStore, floorStore, zoneStore } = useSceneContext(); + const { wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext(); + const { push2D } = undoRedo2DStore(); const { removeWallByPoints, setPosition: setWallPosition, getWallsByPointId } = wallStore(); - const { removeFloorByPoints, setPosition: setFloorPosition, getFloorsByPointId } = floorStore(); - const { removeZoneByPoints, setPosition: setZonePosition, getZonesByPointId } = zoneStore(); + const { removeFloorByPoints, setPosition: setFloorPosition, getFloorsByPointId, getFloorsByPoints } = floorStore(); + const { removeZoneByPoints, setPosition: setZonePosition, getZonesByPointId, getZonesByPoints } = zoneStore(); const { userId, organization } = getUserData(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); @@ -42,6 +43,13 @@ function Line({ points }: Readonly) { const { hoveredLine, setHoveredLine, hoveredPoint } = useBuilderStore(); const { selectedPoints } = useSelectedPoints(); + const [initialPositions, setInitialPositions] = useState<{ + aisles?: Aisle[], + walls?: Wall[], + floors?: Floor[], + zones?: Zone[] + }>({}); + const path = useMemo(() => { const [start, end] = points.map(p => new THREE.Vector3(...p.position)); return new THREE.LineCurve3(start, end); @@ -117,10 +125,26 @@ function Line({ points }: Readonly) { } socket.emit('v1:model-Wall:delete', data); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Line-Delete', + point: { + type: 'Wall', + lineData: removedWall, + timeStamp: new Date().toISOString(), + } + } + ] + }); } + setHoveredLine(null); } if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') { + const Floors = getFloorsByPoints(points); const { removedFloors, updatedFloors } = removeFloorByPoints(points); if (removedFloors.length > 0) { removedFloors.forEach(floor => { @@ -143,6 +167,22 @@ function Line({ points }: Readonly) { socket.emit('v1:model-Floor:delete', data); } }); + + const removedFloorsData = removedFloors.map((floor) => ({ + type: "Floor" as const, + lineData: floor, + timeStamp: new Date().toISOString(), + })); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Lines-Delete', + points: removedFloorsData + } + ] + }); } if (updatedFloors.length > 0) { updatedFloors.forEach(floor => { @@ -165,11 +205,29 @@ function Line({ points }: Readonly) { socket.emit('v1:model-Floor:add', data); } }); + + const updatedFloorsData = updatedFloors.map((floor) => ({ + type: "Floor" as const, + lineData: Floors.find(f => f.floorUuid === floor.floorUuid) || floor, + newData: floor, + timeStamp: new Date().toISOString(), + })); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Lines-Update', + points: updatedFloorsData + } + ] + }); } setHoveredLine(null); } if (points[0].pointType === 'Zone' && points[1].pointType === 'Zone') { + const Zones = getZonesByPoints(points); const { removedZones, updatedZones } = removeZoneByPoints(points); if (removedZones.length > 0) { removedZones.forEach(zone => { @@ -192,6 +250,22 @@ function Line({ points }: Readonly) { socket.emit('v1:zone:delete', data); } }); + + const removedZonesData = removedZones.map((zone) => ({ + type: "Zone" as const, + lineData: zone, + timeStamp: new Date().toISOString(), + })); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Lines-Delete', + points: removedZonesData + } + ] + }); } if (updatedZones.length > 0) { updatedZones.forEach(zone => { @@ -214,7 +288,26 @@ function Line({ points }: Readonly) { socket.emit('v1:zone:add', data); } }); + + const updatedZonesData = updatedZones.map((zone) => ({ + type: "Zone" as const, + lineData: Zones.find(z => z.zoneUuid === zone.zoneUuid) || zone, + newData: zone, + timeStamp: new Date().toISOString(), + })); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Lines-Update', + points: updatedZonesData + } + ] + }); } + + setHoveredLine(null); } handleCanvasCursors('default'); } @@ -267,6 +360,17 @@ 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') { + const floors = getFloorsByPointId(points[0].pointUuid); + setInitialPositions({ floors }); + } else if (points[0].pointType === 'Zone') { + const zones = getZonesByPointId(points[0].pointUuid); + setInitialPositions({ zones }); + } } }; @@ -284,9 +388,7 @@ function Line({ points }: Readonly) { // API - // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall).catch((error) => { - // console.error('Error updating wall:', error); - // }); + // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall); // SOCKET @@ -300,6 +402,23 @@ function Line({ points }: Readonly) { socket.emit('v1:model-Wall:add', data); }) + + if (initialPositions.walls && initialPositions.walls.length > 0) { + const updatedPoints = initialPositions.walls.map((wall) => ({ + type: "Wall" as const, + lineData: wall, + newData: updatedWalls.find(w => w.wallUuid === wall.wallUuid), + timeStamp: new Date().toISOString(), + })); + + push2D({ + type: 'Draw', + actions: [{ + actionType: 'Lines-Update', + points: updatedPoints, + }] + }); + } } } else if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') { const updatedFloors1 = getFloorsByPointId(points[0].pointUuid); @@ -311,9 +430,7 @@ function Line({ points }: Readonly) { // API - // upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor).catch((error) => { - // console.error('Error updating floor:', error); - // }); + // upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor); // SOCKET @@ -327,6 +444,23 @@ function Line({ points }: Readonly) { socket.emit('v1:model-Floor:add', data); }) + + if (initialPositions.floors && initialPositions.floors.length > 0) { + const updatedPoints = initialPositions.floors.map((floor) => ({ + type: "Floor" as const, + lineData: floor, + newData: updatedFloors.find(f => f.floorUuid === floor.floorUuid), + timeStamp: new Date().toISOString(), + })); + + push2D({ + type: 'Draw', + actions: [{ + actionType: 'Lines-Update', + points: updatedPoints, + }] + }); + } } } else if (points[0].pointType === 'Zone' && points[1].pointType === 'Zone') { const updatedZones1 = getZonesByPointId(points[0].pointUuid); @@ -338,9 +472,7 @@ function Line({ points }: Readonly) { // API - // upsertZoneApi(projectId, selectedVersion?.versionId || '', updatedZone).catch((error) => { - // console.error('Error updating zone:', error); - // }); + // upsertZoneApi(projectId, selectedVersion?.versionId || '', updatedZone); // SOCKET @@ -354,6 +486,23 @@ function Line({ points }: Readonly) { socket.emit('v1:zone:add', data); }) + + if (initialPositions.zones && initialPositions.zones.length > 0) { + const updatedPoints = initialPositions.zones.map((zone) => ({ + type: "Zone" as const, + lineData: zone, + newData: updatedZones.find(z => z.zoneUuid === zone.zoneUuid), + timeStamp: new Date().toISOString(), + })); + + push2D({ + type: 'Draw', + actions: [{ + actionType: 'Lines-Update', + points: updatedPoints, + }] + }); + } } } } diff --git a/app/src/modules/builder/point/point.tsx b/app/src/modules/builder/point/point.tsx index 5c62dd5..b203f05 100644 --- a/app/src/modules/builder/point/point.tsx +++ b/app/src/modules/builder/point/point.tsx @@ -32,13 +32,14 @@ function Point({ point }: { readonly point: Point }) { const [dragOffset, setDragOffset] = useState(null); const { socket } = useSocketStore(); const { toolMode } = useToolMode(); - const { aisleStore, wallStore, floorStore, zoneStore } = useSceneContext(); + const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore } = 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 { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle, snapFloorPoint, snapFloorAngle, snapZonePoint, snapZoneAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position }); - const { hoveredPoint,hoveredLine, setHoveredPoint } = useBuilderStore(); + const { hoveredPoint, hoveredLine, setHoveredPoint } = useBuilderStore(); const { selectedPoints } = useSelectedPoints(); const { userId, organization } = getUserData(); const { selectedVersionStore } = useVersionContext(); @@ -47,6 +48,13 @@ function Point({ point }: { readonly point: Point }) { const boxScale: [number, number, number] = Constants.pointConfig.boxScale; const colors = getColor(point); + const [initialPositions, setInitialPositions] = useState<{ + aisles?: Aisle[], + walls?: Wall[], + floors?: Floor[], + zones?: Zone[] + }>({}); + useEffect(() => { handleCanvasCursors('default'); }, [toolMode]) @@ -152,6 +160,20 @@ function Point({ point }: { readonly point: Point }) { const currentPosition = new THREE.Vector3(...point.position); const offset = new THREE.Vector3().subVectors(currentPosition, hit); setDragOffset(offset); + + if (point.pointType === 'Aisle') { + const aisles = getAislesByPointId(point.pointUuid); + setInitialPositions({ aisles }); + } else if (point.pointType === 'Wall') { + const walls = getWallsByPointId(point.pointUuid); + setInitialPositions({ walls }); + } else if (point.pointType === 'Floor') { + const floors = getFloorsByPointId(point.pointUuid); + setInitialPositions({ floors }); + } else if (point.pointType === 'Zone') { + const zones = getZonesByPointId(point.pointUuid); + setInitialPositions({ zones }); + } } }; @@ -159,6 +181,7 @@ function Point({ point }: { readonly point: Point }) { handleCanvasCursors('default'); setDragOffset(null); if (toolMode !== 'move') return; + if (point.pointType === 'Aisle') { const updatedAisles = getAislesByPointId(point.pointUuid); if (updatedAisles.length > 0 && projectId) { @@ -180,6 +203,23 @@ function Point({ point }: { readonly point: Point }) { type: updatedAisle.type }) }) + + if (initialPositions.aisles && initialPositions.aisles.length > 0) { + const updatedPoints = initialPositions.aisles.map((aisle) => ({ + type: "Aisle" as const, + lineData: aisle, + newData: updatedAisles.find(a => a.aisleUuid === aisle.aisleUuid), + timeStamp: new Date().toISOString(), + })); + + push2D({ + type: 'Draw', + actions: [{ + actionType: 'Lines-Update', + points: updatedPoints, + }] + }); + } } } else if (point.pointType === 'Wall') { const updatedWalls = getWallsByPointId(point.pointUuid); @@ -203,6 +243,23 @@ function Point({ point }: { readonly point: Point }) { socket.emit('v1:model-Wall:add', data); }); } + + if (initialPositions.walls && initialPositions.walls.length > 0) { + const updatedPoints = initialPositions.walls.map((wall) => ({ + type: "Wall" as const, + lineData: wall, + newData: updatedWalls.find(w => w.wallUuid === wall.wallUuid), + timeStamp: new Date().toISOString(), + })); + + push2D({ + type: 'Draw', + actions: [{ + actionType: 'Lines-Update', + points: updatedPoints, + }] + }); + } } else if (point.pointType === 'Floor') { const updatedFloors = getFloorsByPointId(point.pointUuid); if (updatedFloors && updatedFloors.length > 0 && projectId) { @@ -225,6 +282,23 @@ function Point({ point }: { readonly point: Point }) { socket.emit('v1:model-Floor:add', data); }); } + + if (initialPositions.floors && initialPositions.floors.length > 0) { + const updatedPoints = initialPositions.floors.map((floor) => ({ + type: "Floor" as const, + lineData: floor, + newData: updatedFloors.find(f => f.floorUuid === floor.floorUuid), + timeStamp: new Date().toISOString(), + })); + + push2D({ + type: 'Draw', + actions: [{ + actionType: 'Lines-Update', + points: updatedPoints, + }] + }); + } } else if (point.pointType === 'Zone') { const updatedZones = getZonesByPointId(point.pointUuid); if (updatedZones && updatedZones.length > 0 && projectId) { @@ -247,13 +321,33 @@ function Point({ point }: { readonly point: Point }) { socket.emit('v1:zone:add', data); }); } + + if (initialPositions.zones && initialPositions.zones.length > 0) { + const updatedPoints = initialPositions.zones.map((zone) => ({ + type: "Zone" as const, + lineData: zone, + newData: updatedZones.find(z => z.zoneUuid === zone.zoneUuid), + timeStamp: new Date().toISOString(), + })); + + push2D({ + type: 'Draw', + actions: [{ + actionType: 'Lines-Update', + points: updatedPoints, + }] + }); + } } + + setInitialPositions({}); } const handlePointClick = (point: Point) => { if (toolMode === '2D-Delete') { if (point.pointType === 'Aisle') { const removedAisles = removeAislePoint(point.pointUuid); + setHoveredPoint(null); if (removedAisles.length > 0) { removedAisles.forEach(aisle => { if (projectId) { @@ -273,9 +367,25 @@ function Point({ point }: { readonly point: Point }) { } socket.emit('v1:model-aisle:delete', data); + } }); - setHoveredPoint(null); + + const removedAislesData = removedAisles.map((aisle) => ({ + type: "Aisle" as const, + lineData: aisle, + timeStamp: new Date().toISOString(), + })); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Lines-Delete', + points: removedAislesData + } + ] + }); } } if (point.pointType === 'Wall') { @@ -302,9 +412,26 @@ function Point({ point }: { readonly point: Point }) { socket.emit('v1:model-Wall:delete', data); } }); + + const removedWallsData = removedWalls.map((wall) => ({ + type: "Wall" as const, + lineData: wall, + timeStamp: new Date().toISOString(), + })); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Lines-Delete', + points: removedWallsData + } + ] + }); } } if (point.pointType === 'Floor') { + const Floors = getFloorsByPointId(point.pointUuid); const { removedFloors, updatedFloors } = removeFloorPoint(point.pointUuid); setHoveredPoint(null); if (removedFloors.length > 0) { @@ -328,6 +455,22 @@ function Point({ point }: { readonly point: Point }) { socket.emit('v1:model-Floor:delete', data); } }); + + const removedFloorsData = removedFloors.map((floor) => ({ + type: "Floor" as const, + lineData: floor, + timeStamp: new Date().toISOString(), + })); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Lines-Delete', + points: removedFloorsData + } + ] + }); } if (updatedFloors.length > 0) { updatedFloors.forEach(floor => { @@ -350,9 +493,27 @@ function Point({ point }: { readonly point: Point }) { socket.emit('v1:model-Floor:add', data); } }); + + const updatedFloorsData = updatedFloors.map((floor) => ({ + type: "Floor" as const, + lineData: Floors.find(f => f.floorUuid === floor.floorUuid) || floor, + newData: floor, + timeStamp: new Date().toISOString(), + })); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Lines-Update', + points: updatedFloorsData + } + ] + }); } } if (point.pointType === 'Zone') { + const Zones = getZonesByPointId(point.pointUuid); const { removedZones, updatedZones } = removeZonePoint(point.pointUuid); setHoveredPoint(null); if (removedZones.length > 0) { @@ -376,6 +537,22 @@ function Point({ point }: { readonly point: Point }) { socket.emit('v1:zone:delete', data); } }); + + const removedZonesData = removedZones.map((zone) => ({ + type: "Zone" as const, + lineData: zone, + timeStamp: new Date().toISOString(), + })); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Lines-Delete', + points: removedZonesData + } + ] + }); } if (updatedZones.length > 0) { updatedZones.forEach(zone => { @@ -398,6 +575,23 @@ function Point({ point }: { readonly point: Point }) { socket.emit('v1:zone:add', data); } }); + + const updatedZonesData = updatedZones.map((zone) => ({ + type: "Zone" as const, + lineData: Zones.find(z => z.zoneUuid === zone.zoneUuid) || zone, + newData: zone, + timeStamp: new Date().toISOString(), + })); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Lines-Update', + points: updatedZonesData + } + ] + }); } } handleCanvasCursors('default'); @@ -422,7 +616,6 @@ function Point({ point }: { readonly point: Point }) { return null; } - return ( <> {!isSelected ? @@ -453,7 +646,7 @@ function Point({ point }: { readonly point: Point }) { onPointerOut={() => { if (hoveredPoint) { setHoveredPoint(null); - if(!hoveredLine){ + if (!hoveredLine) { handleCanvasCursors('default'); } } diff --git a/app/src/modules/builder/wall/wallCreator/wallCreator.tsx b/app/src/modules/builder/wall/wallCreator/wallCreator.tsx index a80a62d..52d5d7e 100644 --- a/app/src/modules/builder/wall/wallCreator/wallCreator.tsx +++ b/app/src/modules/builder/wall/wallCreator/wallCreator.tsx @@ -22,8 +22,9 @@ function WallCreator() { const { toolMode } = useToolMode(); const { activeLayer } = useActiveLayer(); const { socket } = useSocketStore(); - const { wallStore } = useSceneContext(); + const { wallStore, undoRedo2DStore } = useSceneContext(); const { addWall, getWallPointById, removeWall, getWallByPoints } = wallStore(); + const { push2D } = undoRedo2DStore(); const drag = useRef(false); const isLeftMouseDown = useRef(false); const { selectedVersionStore } = useVersionContext(); @@ -91,6 +92,7 @@ function WallCreator() { const closestPoint = new THREE.Vector3().lerpVectors(point1Vec, point2Vec, t); removeWall(wall.wallUuid); + if (projectId) { // API @@ -142,6 +144,7 @@ function WallCreator() { wallHeight: wallHeight, decals: [] } + addWall(wall2); // API @@ -171,8 +174,36 @@ function WallCreator() { wallHeight: wallHeight, decals: [] } + addWall(wall3); + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Lines-Create', + points: [ + { + type: 'Wall', + lineData: wall3, + timeStamp: new Date().toISOString(), + }, { + type: 'Wall', + lineData: wall2, + timeStamp: new Date().toISOString(), + } + ] + }, { + actionType: 'Line-Delete', + point: { + type: 'Wall', + lineData: wall, + timeStamp: new Date().toISOString(), + } + } + ], + }) + // API // if (projectId) { @@ -202,7 +233,8 @@ function WallCreator() { wallThickness: wallThickness, wallHeight: wallHeight, decals: [] - }; + } + addWall(wall1); // API @@ -232,6 +264,7 @@ function WallCreator() { wallHeight: wallHeight, decals: [] } + addWall(wall2); // API @@ -261,8 +294,40 @@ function WallCreator() { wallHeight: wallHeight, decals: [] } + addWall(wall3); + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Lines-Create', + points: [ + { + type: 'Wall', + lineData: wall3, + timeStamp: new Date().toISOString(), + }, { + type: 'Wall', + lineData: wall1, + timeStamp: new Date().toISOString(), + }, { + type: 'Wall', + lineData: wall2, + timeStamp: new Date().toISOString(), + } + ] + }, { + actionType: 'Line-Delete', + point: { + type: 'Wall', + lineData: wall, + timeStamp: new Date().toISOString(), + } + } + ], + }) + // API // if (projectId) { @@ -328,9 +393,24 @@ function WallCreator() { wallThickness: wallThickness, wallHeight: wallHeight, decals: [] - }; + } + addWall(wall); + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Line-Create', + point: { + type: 'Wall', + lineData: wall, + timeStamp: new Date().toISOString(), + } + } + ], + }) + // API // if (projectId) { diff --git a/app/src/modules/builder/zone/zoneCreator/zoneCreator.tsx b/app/src/modules/builder/zone/zoneCreator/zoneCreator.tsx index f1f1b6a..bed501f 100644 --- a/app/src/modules/builder/zone/zoneCreator/zoneCreator.tsx +++ b/app/src/modules/builder/zone/zoneCreator/zoneCreator.tsx @@ -19,8 +19,9 @@ function ZoneCreator() { const { toolMode } = useToolMode(); const { activeLayer } = useActiveLayer(); const { socket } = useSocketStore(); - const { zoneStore } = useSceneContext(); - const { zones, addZone, getZonePointById, getZoneByPoints } = zoneStore(); + const { zoneStore, undoRedo2DStore } = useSceneContext(); + const { addZone, getZonePointById } = zoneStore(); + const { push2D } = undoRedo2DStore(); const drag = useRef(false); const isLeftMouseDown = useRef(false); const { selectedVersionStore } = useVersionContext(); @@ -102,6 +103,21 @@ function ZoneCreator() { }; addZone(zone); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Line-Create', + point: { + type: 'Zone', + lineData: zone, + timeStamp: new Date().toISOString(), + } + } + ], + }) + if (projectId) { // API @@ -139,6 +155,21 @@ function ZoneCreator() { }; addZone(zone); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Line-Create', + point: { + type: 'Zone', + lineData: zone, + timeStamp: new Date().toISOString(), + } + } + ], + }) + if (projectId) { // API @@ -186,6 +217,21 @@ function ZoneCreator() { }; addZone(zone); + + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Line-Create', + point: { + type: 'Zone', + lineData: zone, + timeStamp: new Date().toISOString(), + } + } + ], + }) + if (projectId) { // API @@ -238,7 +284,7 @@ function ZoneCreator() { canvasElement.removeEventListener("click", onMouseClick); canvasElement.removeEventListener("contextmenu", onContext); }; - }, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addZone, getZonePointById, getZoneByPoints, zoneColor, zoneHeight, snappedPosition, snappedPoint]); + }, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addZone, getZonePointById, zoneColor, zoneHeight, snappedPosition, snappedPoint]); return ( <> diff --git a/app/src/modules/scene/controls/controls.tsx b/app/src/modules/scene/controls/controls.tsx index 975ab54..77f234c 100644 --- a/app/src/modules/scene/controls/controls.tsx +++ b/app/src/modules/scene/controls/controls.tsx @@ -14,6 +14,7 @@ import TransformControl from "./transformControls/transformControls"; import { useParams } from "react-router-dom"; import { getUserData } from "../../../functions/getUserData"; import SelectionControls2D from "./selectionControls/selection2D/selectionControls2D"; +import UndoRedo2DControls from "./undoRedoControls/undoRedo2D/undoRedo2DControls"; export default function Controls() { const controlsRef = useRef(null); @@ -142,6 +143,8 @@ export default function Controls() { + + diff --git a/app/src/modules/scene/controls/selectionControls/selection2D/moveControls2D.tsx b/app/src/modules/scene/controls/selectionControls/selection2D/moveControls2D.tsx index fa12f61..f6d04f0 100644 --- a/app/src/modules/scene/controls/selectionControls/selection2D/moveControls2D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection2D/moveControls2D.tsx @@ -36,11 +36,12 @@ function MoveControls2D({ const { projectId } = useParams(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); - const { aisleStore, wallStore, floorStore, zoneStore } = useSceneContext(); - const { setPosition: setAislePosition, getAislesByPointId } = aisleStore(); - const { setPosition: setWallPosition, getWallsByPointId } = wallStore(); - const { setPosition: setFloorPosition, getFloorsByPointId } = floorStore(); - const { setPosition: setZonePosition, getZonesByPointId } = zoneStore(); + const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext(); + const { push2D } = undoRedo2DStore(); + const { setPosition: setAislePosition, getAislesByPointId, getAisleById } = aisleStore(); + const { setPosition: setWallPosition, getWallsByPointId, getWallById } = wallStore(); + const { setPosition: setFloorPosition, getFloorsByPointId, getFloorById } = floorStore(); + const { setPosition: setZonePosition, getZonesByPointId, getZoneById } = zoneStore(); const [dragOffset, setDragOffset] = useState(null); const [initialPositions, setInitialPositions] = useState>({}); const [initialStates, setInitialStates] = useState>({}); @@ -223,6 +224,12 @@ function MoveControls2D({ const placeMovedAssets = () => { if (movedObjects.length === 0) return; + const undoPoints: UndoRedo2DDataTypeSchema[] = []; + const processedAisles: UndoRedo2DDataTypeSchema[] = []; + const processedWalls: UndoRedo2DDataTypeSchema[] = []; + const processedFloors: UndoRedo2DDataTypeSchema[] = []; + const processedZones: UndoRedo2DDataTypeSchema[] = []; + movedObjects.forEach((movedObject: THREE.Object3D) => { if (movedObject.userData.pointUuid) { const point: Point = movedObject.userData as Point; @@ -236,45 +243,84 @@ function MoveControls2D({ // upsertAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || ''); - // SOCKET + // SOCKET socket.emit('v1:model-aisle:add', { - projectId: projectId, + projectId, versionId: selectedVersion?.versionId || '', - userId: userId, - organization: organization, + userId, + organization, aisleUuid: updatedAisle.aisleUuid, points: updatedAisle.points, type: updatedAisle.type - }) - }) + }); + + const old = initialStates[movedObject.uuid]; + if (old) { + processedAisles.push({ + type: 'Aisle', + lineData: { + ...updatedAisle, + points: [ + updatedAisle.points[0].pointUuid === point.pointUuid + ? { ...updatedAisle.points[0], position: [old.position.x, old.position.y, old.position.z] } + : updatedAisle.points[0], + updatedAisle.points[1].pointUuid === point.pointUuid + ? { ...updatedAisle.points[1], position: [old.position.x, old.position.y, old.position.z] } + : updatedAisle.points[1] + ] as [Point, Point], + }, + newData: updatedAisle, + timeStamp: new Date().toISOString(), + }); + } + }); } } else if (point.pointType === 'Wall') { const updatedWalls = getWallsByPointId(point.pointUuid); - if (updatedWalls && updatedWalls.length > 0 && projectId) { - updatedWalls.forEach((updatedWall) => { + if (updatedWalls?.length && projectId) { + updatedWalls.forEach(updatedWall => { // API // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall); - // SOCKET + // SOCKET - const data = { + socket.emit('v1:model-Wall:add', { wallData: updatedWall, - projectId: projectId, + projectId, versionId: selectedVersion?.versionId || '', - userId: userId, - organization: organization - } + userId, + organization + }); - socket.emit('v1:model-Wall:add', data); + const old = initialStates[movedObject.uuid]; + if (old) { + processedWalls.push({ + type: 'Wall', + lineData: { + ...updatedWall, + points: [ + updatedWall.points[0].pointUuid === point.pointUuid + ? { ...updatedWall.points[0], position: [old.position.x, old.position.y, old.position.z] } + : updatedWall.points[0], + updatedWall.points[1].pointUuid === point.pointUuid + ? { ...updatedWall.points[1], position: [old.position.x, old.position.y, old.position.z] } + : updatedWall.points[1] + ] as [Point, Point], + }, + newData: updatedWall, + timeStamp: new Date().toISOString(), + }); + } }); } } else if (point.pointType === 'Floor') { + const Floors = getFloorsByPointId(point.pointUuid); const updatedFloors = getFloorsByPointId(point.pointUuid); - if (updatedFloors && updatedFloors.length > 0 && projectId) { - updatedFloors.forEach((updatedFloor) => { + if (updatedFloors?.length && projectId) { + updatedFloors.forEach(updatedFloor => { // API @@ -282,21 +328,38 @@ function MoveControls2D({ // SOCKET - const data = { + socket.emit('v1:model-Floor:add', { floorData: updatedFloor, - projectId: projectId, + projectId, versionId: selectedVersion?.versionId || '', - userId: userId, - organization: organization - } + userId, + organization + }); - socket.emit('v1:model-Floor:add', data); + const updatedFloorsData = updatedFloors.map((floor) => { + const originalFloor = Floors.find(f => f.floorUuid === floor.floorUuid) || floor; + + const updatedPoints = originalFloor.points.map((pt: Point) => { + const init = initialStates[pt.pointUuid]; + return init ? { ...pt, position: [init.position.x, init.position.y, init.position.z] } : pt; + }) as [Point, Point]; + + return { + type: "Floor" as const, + lineData: { ...originalFloor, points: updatedPoints }, + newData: floor, + timeStamp: new Date().toISOString(), + }; + }); + + processedFloors.push(...updatedFloorsData); }); } } else if (point.pointType === 'Zone') { + const Zones = getZonesByPointId(point.pointUuid); const updatedZones = getZonesByPointId(point.pointUuid); - if (updatedZones && updatedZones.length > 0 && projectId) { - updatedZones.forEach((updatedZone) => { + if (updatedZones?.length && projectId) { + updatedZones.forEach(updatedZone => { // API @@ -304,23 +367,160 @@ function MoveControls2D({ // SOCKET - const data = { + socket.emit('v1:zone:add', { zoneData: updatedZone, - projectId: projectId, + projectId, versionId: selectedVersion?.versionId || '', - userId: userId, - organization: organization - } + userId, + organization + }); - socket.emit('v1:zone:add', data); + const updatedZonesData = updatedZones.map((zone) => { + const originalZone = Zones.find(z => z.zoneUuid === zone.zoneUuid) || zone; + + const updatedPoints = originalZone.points.map((pt: Point) => { + const init = initialStates[pt.pointUuid]; + return init ? { ...pt, position: [init.position.x, init.position.y, init.position.z] } : pt; + }) as [Point, Point]; + + return { + type: "Zone" as const, + lineData: { ...originalZone, points: updatedPoints }, + newData: zone, + timeStamp: new Date().toISOString(), + }; + }); + + processedZones.push(...updatedZonesData); }); } } } - }) + }); + + setTimeout(() => { + if (processedWalls.length > 0) { + const wallMap = new Map(); + + for (const wall of processedWalls) { + if (wall.type !== 'Wall' || !wall.lineData.wallUuid) continue; + const uuid = wall.lineData.wallUuid; + if (!wallMap.has(uuid)) wallMap.set(uuid, []); + wallMap.get(uuid)!.push(wall); + } + + wallMap.forEach((actions, uuid) => { + const hasUpdate = actions.some(action => 'newData' in action); + if (hasUpdate) { + const wallData = getWallById(uuid); + if (wallData) { + undoPoints.push({ + type: 'Wall', + lineData: actions[0].lineData as Wall, + newData: wallData as Wall, + timeStamp: new Date().toISOString() + }); + } + } + }); + } + + if (processedAisles.length > 0) { + const aisleMap = new Map(); + + for (const aisle of processedAisles) { + if (aisle.type !== 'Aisle' || !aisle.lineData.aisleUuid) continue; + const uuid = aisle.lineData.aisleUuid; + if (!aisleMap.has(uuid)) aisleMap.set(uuid, []); + aisleMap.get(uuid)!.push(aisle); + } + + aisleMap.forEach((actions, uuid) => { + const hasUpdate = actions.some(action => 'newData' in action); + if (hasUpdate) { + const aisleData = getAisleById(uuid); + if (aisleData) { + undoPoints.push({ + type: 'Aisle', + lineData: actions[0].lineData as Aisle, + newData: aisleData as Aisle, + timeStamp: new Date().toISOString() + }); + } + } + }); + } + + if (processedFloors.length > 0) { + const floorMap = new Map(); + + for (const floor of processedFloors) { + if (floor.type !== 'Floor' || !floor.lineData.floorUuid) continue; + const uuid = floor.lineData.floorUuid; + if (!floorMap.has(uuid)) { + floorMap.set(uuid, []); + } + floorMap.get(uuid)!.push(floor); + } + + floorMap.forEach((actions, uuid) => { + const hasUpdate = actions.some(action => 'newData' in action); + if (hasUpdate) { + const floorData = getFloorById(uuid); + if (floorData) { + undoPoints.push({ + type: 'Floor', + lineData: actions[0].lineData as Floor, + newData: floorData as Floor, + timeStamp: new Date().toISOString() + }); + } + } + }); + } + + if (processedZones.length > 0) { + const zoneMap = new Map(); + + for (const zone of processedZones) { + if (zone.type !== 'Zone' || !zone.lineData.zoneUuid) continue; + const uuid = zone.lineData.zoneUuid; + if (!zoneMap.has(uuid)) { + zoneMap.set(uuid, []); + } + zoneMap.get(uuid)!.push(zone); + } + + zoneMap.forEach((actions, uuid) => { + const hasUpdate = actions.some(action => 'newData' in action); + if (hasUpdate) { + const zoneData = getZoneById(uuid); + if (zoneData) { + undoPoints.push({ + type: 'Zone', + lineData: actions[0].lineData as Zone, + newData: zoneData as Zone, + timeStamp: new Date().toISOString() + }); + } + } + }); + } + + if (undoPoints.length > 0) { + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Lines-Update', + points: undoPoints + } + ] + }); + } + }, 0); echo.success("Object moved!"); - clearSelection(); }; diff --git a/app/src/modules/scene/controls/selectionControls/selection2D/selectionControls2D.tsx b/app/src/modules/scene/controls/selectionControls/selection2D/selectionControls2D.tsx index 9d90d3c..3ca6f99 100644 --- a/app/src/modules/scene/controls/selectionControls/selection2D/selectionControls2D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection2D/selectionControls2D.tsx @@ -22,7 +22,7 @@ import MoveControls2D from "./moveControls2D"; // import { upsertZoneApi } from "../../../../../services/factoryBuilder/zone/upsertZoneApi"; const SelectionControls2D: React.FC = () => { - const { camera, controls, gl, scene, raycaster, pointer } = useThree(); + const { camera, controls, gl, scene, pointer } = useThree(); const { toggleView } = useToggleView(); const { selectedPoints, setSelectedPoints, clearSelectedPoints } = useSelectedPoints(); const [movedObjects, setMovedObjects] = useState([]); @@ -38,11 +38,12 @@ const SelectionControls2D: React.FC = () => { const { selectedVersion } = selectedVersionStore(); const { projectId } = useParams(); const { hoveredLine, hoveredPoint } = useBuilderStore(); - const { aisleStore, wallStore, floorStore, zoneStore } = useSceneContext(); + const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext(); + const { push2D } = undoRedo2DStore(); const { removePoint: removeAislePoint } = aisleStore(); const { removePoint: removeWallPoint } = wallStore(); - const { removePoint: removeFloorPoint } = floorStore(); - const { removePoint: removeZonePoint } = zoneStore(); + const { removePoint: removeFloorPoint, getFloorsByPointId, getFloorById } = floorStore(); + const { removePoint: removeZonePoint, getZonesByPointId, getZoneById } = zoneStore(); const isDragging = useRef(false); const isLeftMouseDown = useRef(false); @@ -223,6 +224,13 @@ const SelectionControls2D: React.FC = () => { const deleteSelection = () => { if (selectedPoints.length > 0 && duplicatedObjects.length === 0) { + const deletedPoints: UndoRedo2DDataTypeSchema[] = []; + const updatedPoints: UndoRedo2DDataTypeSchema[] = []; + const processedAisles: UndoRedo2DDataTypeSchema[] = []; + const processedWalls: UndoRedo2DDataTypeSchema[] = []; + const processedFloors: UndoRedo2DDataTypeSchema[] = []; + const processedZones: UndoRedo2DDataTypeSchema[] = []; + selectedPoints.forEach((selectedPoint) => { if (selectedPoint.userData.pointUuid) { const point: Point = selectedPoint.userData as Point; @@ -249,6 +257,14 @@ const SelectionControls2D: React.FC = () => { socket.emit('v1:model-aisle:delete', data); } }); + + const removedAislesData = removedAisles.map((aisle) => ({ + type: "Aisle" as const, + lineData: aisle, + timeStamp: new Date().toISOString(), + })); + + processedAisles.push(...removedAislesData); } } if (point.pointType === 'Wall') { @@ -274,9 +290,18 @@ const SelectionControls2D: React.FC = () => { socket.emit('v1:model-Wall:delete', data); } }); + + const removedWallsData = removedWalls.map((wall) => ({ + type: "Wall" as const, + lineData: wall, + timeStamp: new Date().toISOString(), + })); + + processedWalls.push(...removedWallsData); } } if (point.pointType === 'Floor') { + const Floors = getFloorsByPointId(point.pointUuid); const { removedFloors, updatedFloors } = removeFloorPoint(point.pointUuid); if (removedFloors.length > 0) { removedFloors.forEach(floor => { @@ -299,6 +324,14 @@ const SelectionControls2D: React.FC = () => { socket.emit('v1:model-Floor:delete', data); } }); + + const removedFloorsData = removedFloors.map((floor) => ({ + type: "Floor" as const, + lineData: floor, + timeStamp: new Date().toISOString(), + })); + + processedFloors.push(...removedFloorsData); } if (updatedFloors.length > 0) { updatedFloors.forEach(floor => { @@ -321,9 +354,19 @@ const SelectionControls2D: React.FC = () => { socket.emit('v1:model-Floor:add', data); } }); + + const updatedFloorsData = updatedFloors.map((floor) => ({ + type: "Floor" as const, + lineData: Floors.find(f => f.floorUuid === floor.floorUuid) || floor, + newData: floor, + timeStamp: new Date().toISOString(), + })); + + processedFloors.push(...updatedFloorsData); } } if (point.pointType === 'Zone') { + const Zones = getZonesByPointId(point.pointUuid); const { removedZones, updatedZones } = removeZonePoint(point.pointUuid); if (removedZones.length > 0) { removedZones.forEach(zone => { @@ -346,6 +389,14 @@ const SelectionControls2D: React.FC = () => { socket.emit('v1:zone:delete', data); } }); + + const removedZonesData = removedZones.map((zone) => ({ + type: "Zone" as const, + lineData: zone, + timeStamp: new Date().toISOString(), + })); + + processedZones.push(...removedZonesData); } if (updatedZones.length > 0) { updatedZones.forEach(zone => { @@ -368,11 +419,173 @@ const SelectionControls2D: React.FC = () => { socket.emit('v1:zone:add', data); } }); + + const updatedZonesData = updatedZones.map((zone) => ({ + type: "Zone" as const, + lineData: Zones.find(z => z.zoneUuid === zone.zoneUuid) || zone, + newData: zone, + timeStamp: new Date().toISOString(), + })); + + processedZones.push(...updatedZonesData); } } } }) + setTimeout(() => { + if (processedWalls.length > 0) { + const wallMap = new Map(); + + for (const wall of processedWalls) { + if (wall.type !== 'Wall' || !wall.lineData.wallUuid) continue; + const uuid = wall.lineData.wallUuid; + if (!wallMap.has(uuid)) wallMap.set(uuid, []); + wallMap.get(uuid)!.push(wall); + } + + wallMap.forEach((actions) => { + const hasDelete = actions.some(action => !('newData' in action)); + if (hasDelete) { + deletedPoints.push({ + type: 'Wall', + lineData: actions[0].lineData as Wall, + timeStamp: new Date().toISOString() + }); + } + }); + } + + if (processedAisles.length > 0) { + const aisleMap = new Map(); + + for (const aisle of processedAisles) { + if (aisle.type !== 'Aisle' || !aisle.lineData.aisleUuid) continue; + const uuid = aisle.lineData.aisleUuid; + if (!aisleMap.has(uuid)) aisleMap.set(uuid, []); + aisleMap.get(uuid)!.push(aisle); + } + + aisleMap.forEach((actions) => { + const hasDelete = actions.some(action => !('newData' in action)); + if (hasDelete) { + deletedPoints.push({ + type: 'Aisle', + lineData: actions[0].lineData as Aisle, + timeStamp: new Date().toISOString() + }); + } + }); + } + + if (processedFloors.length > 0) { + const floorMap = new Map(); + + for (const floor of processedFloors) { + if (floor.type !== 'Floor' || !floor.lineData.floorUuid) return; + const uuid = floor.lineData.floorUuid; + if (!floorMap.has(uuid)) { + floorMap.set(uuid, []); + } + floorMap.get(uuid)!.push(floor); + } + + floorMap.forEach((actions, uuid) => { + const hasDelete = actions.some(action => !('newData' in action)); + const hasUpdate = actions.some(action => 'newData' in action); + + if (hasDelete) { + deletedPoints.push({ + type: 'Floor', + lineData: actions[0].lineData as Floor, + timeStamp: new Date().toISOString() + }); + } else if (!hasDelete && hasUpdate) { + const floorData = getFloorById(uuid); + if (floorData) { + updatedPoints.push({ + type: 'Floor', + lineData: actions[0].lineData as Floor, + newData: floorData as Floor, + timeStamp: new Date().toISOString() + }); + } + } + }); + } + + if (processedZones.length > 0) { + const zoneMap = new Map(); + + for (const zone of processedZones) { + if (zone.type !== 'Zone' || !zone.lineData.zoneUuid) return; + const uuid = zone.lineData.zoneUuid; + if (!zoneMap.has(uuid)) { + zoneMap.set(uuid, []); + } + zoneMap.get(uuid)!.push(zone); + } + + zoneMap.forEach((actions, uuid) => { + const hasDelete = actions.some(action => !('newData' in action)); + const hasUpdate = actions.some(action => 'newData' in action); + + if (hasDelete) { + deletedPoints.push({ + type: 'Zone', + lineData: actions[0].lineData as Zone, + timeStamp: new Date().toISOString() + }); + } else if (!hasDelete && hasUpdate) { + const zoneData = getZoneById(uuid); + if (zoneData) { + updatedPoints.push({ + type: 'Zone', + lineData: actions[0].lineData as Zone, + newData: zoneData as Zone, + timeStamp: new Date().toISOString() + }); + } + } + }); + } + + if (deletedPoints.length > 0 && updatedPoints.length > 0) { + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Lines-Delete', + points: deletedPoints + }, + { + actionType: 'Lines-Update', + points: updatedPoints + } + ] + }); + } else if (deletedPoints.length > 0 && updatedPoints.length === 0) { + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Lines-Delete', + points: deletedPoints + } + ] + }); + } else if (updatedPoints.length > 0 && deletedPoints.length === 0) { + push2D({ + type: 'Draw', + actions: [ + { + actionType: 'Lines-Update', + points: updatedPoints + } + ] + }); + } + }, 0); } echo.success("Selected points removed!"); clearSelection(); @@ -380,6 +593,7 @@ const SelectionControls2D: React.FC = () => { return ( <> + diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/copyPasteControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/copyPasteControls3D.tsx index 24a5f58..a147750 100644 --- a/app/src/modules/scene/controls/selectionControls/selection3D/copyPasteControls3D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection3D/copyPasteControls3D.tsx @@ -213,7 +213,7 @@ const CopyPasteControls3D = ({ modelUuid: pastedAsset.userData.modelUuid, modelName: pastedAsset.userData.modelName, assetId: pastedAsset.userData.assetId, - position: asset.position, + position: [position.x, position.y, position.z], rotation: { x: asset.rotation[0], y: asset.rotation[1], z: asset.rotation[2] }, isLocked: false, isVisible: true, diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/duplicationControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/duplicationControls3D.tsx index 5ab6b75..55e88fb 100644 --- a/app/src/modules/scene/controls/selectionControls/selection3D/duplicationControls3D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection3D/duplicationControls3D.tsx @@ -214,7 +214,7 @@ const DuplicationControls3D = ({ modelUuid: duplicatedAsset.userData.modelUuid, modelName: duplicatedAsset.userData.modelName, assetId: duplicatedAsset.userData.assetId, - position: asset.position, + position: [position.x, position.y, position.z], rotation: { x: asset.rotation[0], y: asset.rotation[1], z: asset.rotation[2] }, isLocked: false, isVisible: true, diff --git a/app/src/modules/scene/controls/undoRedoControls/handlers/useRedoHandler.ts b/app/src/modules/scene/controls/undoRedoControls/handlers/useRedoHandler.ts new file mode 100644 index 0000000..77ad0d6 --- /dev/null +++ b/app/src/modules/scene/controls/undoRedoControls/handlers/useRedoHandler.ts @@ -0,0 +1,355 @@ +import { useParams } from "react-router-dom"; +import { getUserData } from "../../../../../functions/getUserData"; +import { useVersionContext } from "../../../../builder/version/versionContext"; +import { useSceneContext } from "../../../sceneContext"; +import { useSocketStore } from "../../../../../store/builder/store"; + +// import { upsertWallApi } from "../../../../../services/factoryBuilder/wall/upsertWallApi"; +// import { deleteWallApi } from "../../../../../services/factoryBuilder/wall/deleteWallApi"; + +// import { upsertZoneApi } from "../../../../../services/factoryBuilder/zone/upsertZoneApi"; +// import { deleteZoneApi } from "../../../../../services/factoryBuilder/zone/deleteZoneApi"; + +// import { upsertFloorApi } from "../../../../../services/factoryBuilder/floor/upsertFloorApi"; +// import { deleteFloorApi } from "../../../../../services/factoryBuilder/floor/deleteFloorApi"; + +// import { upsertAisleApi } from "../../../../../services/factoryBuilder/aisle/upsertAisleApi"; +// import { deleteAisleApi } from "../../../../../services/factoryBuilder/aisle/deleteAisleApi"; + +function useRedoHandler() { + const { undoRedo2DStore, wallStore, floorStore, zoneStore, aisleStore } = useSceneContext(); + const { redo2D, peekRedo2D } = undoRedo2DStore(); + const { addWall, removeWall, updateWall } = wallStore(); + const { addFloor, removeFloor, updateFloor } = floorStore(); + const { addZone, removeZone, updateZone } = zoneStore(); + const { addAisle, removeAisle, updateAisle } = aisleStore(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { userId, organization } = getUserData(); + const { projectId } = useParams(); + const { socket } = useSocketStore(); + + const handleRedo = () => { + const redoData = peekRedo2D(); + if (!redoData) return; + + if (redoData.type === 'Draw') { + const { actions } = redoData; + + actions.forEach(action => { + const { actionType } = action; + + if ('point' in action) { + const point = action.point; + + if (actionType === 'Line-Create') { + handleCreate(point); + } else if (actionType === 'Line-Update') { + handleUpdate(point); + } else if (actionType === 'Line-Delete') { + handleRemove(point); + } + + } else if ('points' in action) { + const points = action.points; + + if (actionType === 'Lines-Create') { + points.forEach(handleCreate); + } else if (actionType === 'Lines-Update') { + points.forEach(handleUpdate); + } else if (actionType === 'Lines-Delete') { + points.forEach(handleRemove); + } + } + }); + } else if (redoData.type === 'UI') { + // Handle UI actions if needed + } + + redo2D(); + }; + + const handleCreate = (point: UndoRedo2DDataTypeSchema) => { + switch (point.type) { + case 'Wall': createWallFromBackend(point.lineData); break; + case 'Floor': createFloorFromBackend(point.lineData); break; + case 'Zone': createZoneFromBackend(point.lineData); break; + case 'Aisle': createAisleFromBackend(point.lineData); break; + } + }; + + const handleRemove = (point: UndoRedo2DDataTypeSchema) => { + switch (point.type) { + case 'Wall': removeWallFromBackend(point.lineData.wallUuid); break; + case 'Floor': removeFloorFromBackend(point.lineData.floorUuid); break; + case 'Zone': removeZoneFromBackend(point.lineData.zoneUuid); break; + case 'Aisle': removeAisleFromBackend(point.lineData.aisleUuid); break; + } + }; + + const handleUpdate = (point: UndoRedo2DDataTypeSchema) => { + if (!point.newData) return; + switch (point.type) { + case 'Wall': updateWallFromBackend(point.newData.wallUuid, point.newData); break; + case 'Floor': updateFloorFromBackend(point.newData.floorUuid, point.newData); break; + case 'Zone': updateZoneFromBackend(point.newData.zoneUuid, point.newData); break; + case 'Aisle': updateAisleFromBackend(point.newData.aisleUuid, point.newData); break; + } + }; + + const createWallFromBackend = (wallData: Wall) => { + addWall(wallData); + if (projectId) { + // API + + // upsertWallApi(projectId, selectedVersion?.versionId || '', wallData); + + // SOCKET + + const data = { + wallData: wallData, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + } + }; + + const removeWallFromBackend = (wallUuid: string) => { + removeWall(wallUuid); + if (projectId) { + // API + + // deleteWallApi(projectId, selectedVersion?.versionId || '', wallUuid); + + // SOCKET + + const data = { + wallUuid: wallUuid, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:delete', data); + } + }; + + const updateWallFromBackend = (wallUuid: string, updatedData: Wall) => { + updateWall(wallUuid, updatedData); + if (projectId) { + // API + + // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedData); + + // SOCKET + + const data = { + wallData: updatedData, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + } + }; + + const createFloorFromBackend = (floorData: Floor) => { + addFloor(floorData); + if (projectId) { + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', floorData); + + // SOCKET + + const data = { + floorData: floorData, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + } + }; + + const removeFloorFromBackend = (floorUuid: string) => { + removeFloor(floorUuid); + if (projectId) { + // API + + // deleteFloorApi(projectId, selectedVersion?.versionId || '', floorUuid); + + // SOCKET + + const data = { + floorUuid: floorUuid, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:delete', data); + } + }; + + const updateFloorFromBackend = (floorUuid: string, updatedData: Floor) => { + updateFloor(floorUuid, updatedData); + if (projectId) { + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedData); + + // SOCKET + + const data = { + floorData: updatedData, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + } + }; + + const createZoneFromBackend = (zoneData: Zone) => { + addZone(zoneData); + if (projectId) { + // API + + // upsertZoneApi(projectId, selectedVersion?.versionId || '', zoneData); + + // SOCKET + + const data = { + zoneData: zoneData, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:add', data); + } + }; + + const removeZoneFromBackend = (zoneUuid: string) => { + removeZone(zoneUuid); + if (projectId) { + // API + + // deleteZoneApi(projectId, selectedVersion?.versionId || '', zoneUuid); + + // SOCKET + + const data = { + zoneUuid, + projectId, + versionId: selectedVersion?.versionId || '', + userId, + organization + }; + + socket.emit('v1:zone:delete', data); + } + }; + + const updateZoneFromBackend = (zoneUuid: string, updatedData: Zone) => { + updateZone(zoneUuid, updatedData); + if (projectId) { + // API + + // upsertZoneApi(projectId, selectedVersion?.versionId || '', updatedData); + + // SOCKET + + const data = { + zoneData: updatedData, + projectId, + versionId: selectedVersion?.versionId || '', + userId, + organization + }; + + socket.emit('v1:zone:add', data); + } + }; + + const createAisleFromBackend = (aisleData: Aisle) => { + addAisle(aisleData); + if (projectId) { + // API + + // upsertAisleApi(projectId, selectedVersion?.versionId || '', aisleData); + + // SOCKET + + const data = { + ...aisleData, + projectId, + versionId: selectedVersion?.versionId || '', + userId, + organization + }; + + socket.emit('v1:model-aisle:add', data); + } + }; + + const removeAisleFromBackend = (aisleUuid: string) => { + removeAisle(aisleUuid); + if (projectId) { + // API + + // deleteAisleApi(projectId, selectedVersion?.versionId || '', aisleUuid); + + // SOCKET + + const data = { + aisleUuid, + projectId, + versionId: selectedVersion?.versionId || '', + userId, + organization + }; + + socket.emit('v1:model-aisle:delete', data); + } + }; + + const updateAisleFromBackend = (aisleUuid: string, updatedData: Aisle) => { + updateAisle(aisleUuid, updatedData); + if (projectId) { + // API + + // upsertAisleApi(projectId, selectedVersion?.versionId || '', updatedData); + + // SOCKET + + const data = { + aisleData: updatedData, + projectId, + versionId: selectedVersion?.versionId || '', + userId, + organization + }; + + socket.emit('v1:model-aisle:add', data); + } + }; + + return { handleRedo }; +} + +export default useRedoHandler; diff --git a/app/src/modules/scene/controls/undoRedoControls/handlers/useUndoHandler.ts b/app/src/modules/scene/controls/undoRedoControls/handlers/useUndoHandler.ts new file mode 100644 index 0000000..de9f3b1 --- /dev/null +++ b/app/src/modules/scene/controls/undoRedoControls/handlers/useUndoHandler.ts @@ -0,0 +1,356 @@ +import { useParams } from "react-router-dom"; +import { getUserData } from "../../../../../functions/getUserData"; +import { useVersionContext } from "../../../../builder/version/versionContext"; +import { useSceneContext } from "../../../sceneContext"; +import { useSocketStore } from "../../../../../store/builder/store"; + +// import { upsertWallApi } from "../../../../../services/factoryBuilder/wall/upsertWallApi"; +// import { deleteWallApi } from "../../../../../services/factoryBuilder/wall/deleteWallApi"; + +// import { upsertZoneApi } from "../../../../../services/factoryBuilder/zone/upsertZoneApi"; +// import { deleteWallApi } from "../../../../../services/factoryBuilder/wall/deleteWallApi"; + +// import { upsertFloorApi } from "../../../../../services/factoryBuilder/floor/upsertFloorApi"; +// import { deleteFloorApi } from "../../../../../services/factoryBuilder/floor/deleteFloorApi"; + +// import { upsertAisleApi } from "../../../../../services/factoryBuilder/aisle/upsertAisleApi"; +// import { deleteAisleApi } from "../../../../../services/factoryBuilder/aisle/deleteAisleApi"; + +function useUndoHandler() { + const { undoRedo2DStore, wallStore, floorStore, zoneStore, aisleStore } = useSceneContext(); + const { undo2D, peekUndo2D } = undoRedo2DStore(); + const { addWall, removeWall, updateWall } = wallStore(); + const { addFloor, removeFloor, updateFloor } = floorStore(); + const { addZone, removeZone, updateZone } = zoneStore(); + const { addAisle, removeAisle, updateAisle } = aisleStore(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { userId, organization } = getUserData(); + const { projectId } = useParams(); + const { socket } = useSocketStore(); + + const handleUndo = () => { + const unDoData = peekUndo2D(); + if (!unDoData) return; + + if (unDoData.type === 'Draw') { + const { actions } = unDoData; + + actions.forEach(action => { + const { actionType } = action; + + if ('point' in action) { + const point = action.point; + + if (actionType === 'Line-Create') { + handleRemove(point); + } else if (actionType === 'Line-Update') { + handleUpdate(point); + } else if (actionType === 'Line-Delete') { + handleCreate(point); + } + + } else if ('points' in action) { + const points = action.points; + + if (actionType === 'Lines-Create') { + points.forEach(handleRemove); + } else if (actionType === 'Lines-Update') { + points.forEach(handleUpdate); + } else if (actionType === 'Lines-Delete') { + points.forEach(handleCreate); + } + } + }); + } else if (unDoData.type === 'UI') { + // Handle UI actions if needed + } + + undo2D(); + + }; + + const handleCreate = (point: UndoRedo2DDataTypeSchema) => { + switch (point.type) { + case 'Wall': createWallFromBackend(point.lineData); break; + case 'Floor': createFloorFromBackend(point.lineData); break; + case 'Zone': createZoneFromBackend(point.lineData); break; + case 'Aisle': createAisleFromBackend(point.lineData); break; + } + }; + + const handleRemove = (point: UndoRedo2DDataTypeSchema) => { + switch (point.type) { + case 'Wall': removeWallFromBackend(point.lineData.wallUuid); break; + case 'Floor': removeFloorFromBackend(point.lineData.floorUuid); break; + case 'Zone': removeZoneFromBackend(point.lineData.zoneUuid); break; + case 'Aisle': removeAisleFromBackend(point.lineData.aisleUuid); break; + } + }; + + const handleUpdate = (point: UndoRedo2DDataTypeSchema) => { + switch (point.type) { + case 'Wall': updateWallFromBackend(point.lineData.wallUuid, point.lineData); break; + case 'Floor': updateFloorFromBackend(point.lineData.floorUuid, point.lineData); break; + case 'Zone': updateZoneFromBackend(point.lineData.zoneUuid, point.lineData); break; + case 'Aisle': updateAisleFromBackend(point.lineData.aisleUuid, point.lineData); break; + } + }; + + + const createWallFromBackend = (wallData: Wall) => { + addWall(wallData); + if (projectId) { + // API + + // upsertWallApi(projectId, selectedVersion?.versionId || '', wallData); + + // SOCKET + + const data = { + wallData: wallData, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + } + }; + + const removeWallFromBackend = (wallUuid: string) => { + removeWall(wallUuid); + if (projectId) { + // API + + // deleteWallApi(projectId, selectedVersion?.versionId || '', wallUuid); + + // SOCKET + + const data = { + wallUuid: wallUuid, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:delete', data); + } + }; + + const updateWallFromBackend = (wallUuid: string, updatedData: Wall) => { + updateWall(wallUuid, updatedData); + if (projectId) { + // API + + // upsertWallApi(projectId, selectedVersion?.versionId || '', updatedData); + + // SOCKET + + const data = { + wallData: updatedData, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Wall:add', data); + } + }; + + const createFloorFromBackend = (floorData: Floor) => { + addFloor(floorData); + if (projectId) { + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', floorData); + + // SOCKET + + const data = { + floorData: floorData, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + } + }; + + const removeFloorFromBackend = (floorUuid: string) => { + removeFloor(floorUuid); + if (projectId) { + // API + + // deleteFloorApi(projectId, selectedVersion?.versionId || '', floorUuid); + + // SOCKET + + const data = { + floorUuid: floorUuid, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:delete', data); + } + }; + + const updateFloorFromBackend = (floorUuid: string, updatedData: Floor) => { + updateFloor(floorUuid, updatedData); + if (projectId) { + // API + + // upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedData); + + // SOCKET + + const data = { + floorData: updatedData, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:model-Floor:add', data); + } + }; + + const createZoneFromBackend = (zoneData: Zone) => { + addZone(zoneData); + if (projectId) { + // API + + // upsertZoneApi(projectId, selectedVersion?.versionId || '', zoneData); + + // SOCKET + + const data = { + zoneData: zoneData, + projectId: projectId, + versionId: selectedVersion?.versionId || '', + userId: userId, + organization: organization + } + + socket.emit('v1:zone:add', data); + } + }; + + const removeZoneFromBackend = (zoneUuid: string) => { + removeZone(zoneUuid); + if (projectId) { + // API + + // deleteZoneApi(projectId, selectedVersion?.versionId || '', zoneUuid); + + // SOCKET + + const data = { + zoneUuid, + projectId, + versionId: selectedVersion?.versionId || '', + userId, + organization + }; + + socket.emit('v1:zone:delete', data); + } + }; + + const updateZoneFromBackend = (zoneUuid: string, updatedData: Zone) => { + updateZone(zoneUuid, updatedData); + if (projectId) { + // API + + // upsertZoneApi(projectId, selectedVersion?.versionId || '', updatedData); + + // SOCKET + + const data = { + zoneData: updatedData, + projectId, + versionId: selectedVersion?.versionId || '', + userId, + organization + }; + + socket.emit('v1:zone:add', data); + } + }; + + const createAisleFromBackend = (aisleData: Aisle) => { + addAisle(aisleData); + if (projectId) { + // API + + // upsertAisleApi(projectId, selectedVersion?.versionId || '', aisleData); + + // SOCKET + + const data = { + ...aisleData, + projectId, + versionId: selectedVersion?.versionId || '', + userId, + organization + }; + + socket.emit('v1:model-aisle:add', data); + } + }; + + const removeAisleFromBackend = (aisleUuid: string) => { + removeAisle(aisleUuid); + if (projectId) { + // API + + // deleteAisleApi(projectId, selectedVersion?.versionId || '', aisleUuid); + + // SOCKET + + const data = { + aisleUuid, + projectId, + versionId: selectedVersion?.versionId || '', + userId, + organization + }; + + socket.emit('v1:model-aisle:delete', data); + } + }; + + const updateAisleFromBackend = (aisleUuid: string, updatedData: Aisle) => { + updateAisle(aisleUuid, updatedData); + if (projectId) { + // API + + // upsertAisleApi(projectId, selectedVersion?.versionId || '', updatedData); + + // SOCKET + + const data = { + aisleData: updatedData, + projectId, + versionId: selectedVersion?.versionId || '', + userId, + organization + }; + + socket.emit('v1:model-aisle:add', data); + } + }; + + return { handleUndo }; +} + +export default useUndoHandler; \ No newline at end of file diff --git a/app/src/modules/scene/controls/undoRedoControls/undoRedo2D/undoRedo2DControls.tsx b/app/src/modules/scene/controls/undoRedoControls/undoRedo2D/undoRedo2DControls.tsx new file mode 100644 index 0000000..d45518c --- /dev/null +++ b/app/src/modules/scene/controls/undoRedoControls/undoRedo2D/undoRedo2DControls.tsx @@ -0,0 +1,49 @@ +import { useEffect } from 'react' +import { useSceneContext } from '../../../sceneContext' +import { detectModifierKeys } from '../../../../../utils/shortcutkeys/detectModifierKeys'; +import { useSocketStore, useToggleView } from '../../../../../store/builder/store'; +import { useVersionContext } from '../../../../builder/version/versionContext'; + +import useUndoHandler from '../handlers/useUndoHandler'; +import useRedoHandler from '../handlers/useRedoHandler'; + +function UndoRedo2DControls() { + const { undoRedo2DStore } = useSceneContext(); + const { undoStack, redoStack } = undoRedo2DStore(); + const { toggleView } = useToggleView(); + const { handleUndo } = useUndoHandler(); + const { handleRedo } = useRedoHandler(); + const { socket } = useSocketStore(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + + useEffect(() => { + console.log(undoStack, redoStack); + }, [undoStack, redoStack]); + + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + const keyCombination = detectModifierKeys(event); + + if (keyCombination === 'Ctrl+Z') { + handleUndo(); + } + + if (keyCombination === 'Ctrl+Y') { + handleRedo(); + } + }; + + if (toggleView) { + window.addEventListener('keydown', handleKeyDown); + } + + return () => { + window.removeEventListener('keydown', handleKeyDown); + }; + }, [toggleView, undoStack, redoStack, socket, selectedVersion]); + + return null; +} + +export default UndoRedo2DControls; diff --git a/app/src/modules/scene/sceneContext.tsx b/app/src/modules/scene/sceneContext.tsx index 187d66a..bf65c36 100644 --- a/app/src/modules/scene/sceneContext.tsx +++ b/app/src/modules/scene/sceneContext.tsx @@ -7,6 +7,8 @@ import { createAisleStore, AisleStoreType } from '../../store/builder/useAisleSt import { createZoneStore, ZoneStoreType } from '../../store/builder/useZoneStore'; import { createFloorStore, FloorStoreType } from '../../store/builder/useFloorStore'; +import { createUndoRedo2DStore, UndoRedo2DStoreType } from '../../store/builder/useUndoRedo2DStore'; + import { createEventStore, EventStoreType } from '../../store/simulation/useEventsStore'; import { createProductStore, ProductStoreType } from '../../store/simulation/useProductStore'; @@ -27,6 +29,8 @@ type SceneContextValue = { zoneStore: ZoneStoreType, floorStore: FloorStoreType, + undoRedo2DStore: UndoRedo2DStoreType, + eventStore: EventStoreType, productStore: ProductStoreType, @@ -62,6 +66,8 @@ export function SceneProvider({ const zoneStore = useMemo(() => createZoneStore(), []); const floorStore = useMemo(() => createFloorStore(), []); + const undoRedo2DStore = useMemo(() => createUndoRedo2DStore(), []); + const eventStore = useMemo(() => createEventStore(), []); const productStore = useMemo(() => createProductStore(), []); @@ -82,6 +88,7 @@ export function SceneProvider({ aisleStore.getState().clearAisles(); zoneStore.getState().clearZones(); floorStore.getState().clearFloors(); + undoRedo2DStore.getState().clearUndoRedo2D(); eventStore.getState().clearEvents(); productStore.getState().clearProducts(); materialStore.getState().clearMaterials(); @@ -92,7 +99,7 @@ export function SceneProvider({ storageUnitStore.getState().clearStorageUnits(); humanStore.getState().clearHumans(); humanEventManagerRef.current.humanStates = []; - }, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore]); + }, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, undoRedo2DStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore]); const contextValue = useMemo(() => ( { @@ -102,6 +109,7 @@ export function SceneProvider({ aisleStore, zoneStore, floorStore, + undoRedo2DStore, eventStore, productStore, materialStore, @@ -115,7 +123,7 @@ export function SceneProvider({ clearStores, layout } - ), [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, clearStores, layout]); + ), [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, undoRedo2DStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, clearStores, layout]); return ( diff --git a/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts b/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts index e951289..0373fd0 100644 --- a/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts +++ b/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts @@ -3,9 +3,10 @@ import { useFrame } from "@react-three/fiber"; import { usePlayButtonStore, usePauseButtonStore, useResetButtonStore, useAnimationPlaySpeed } from "../../../../../store/usePlayButtonStore"; import { useSceneContext } from "../../../../scene/sceneContext"; import { useProductContext } from "../../../products/productContext"; +import { useHumanEventManager } from "../../../human/eventManager/useHumanEventManager"; export function useRetrieveHandler() { - const { materialStore, armBotStore, vehicleStore, storageUnitStore, productStore, humanStore, assetStore } = useSceneContext(); + const { materialStore, armBotStore, machineStore, vehicleStore, storageUnitStore, productStore, humanStore, assetStore } = useSceneContext(); const { selectedProductStore } = useProductContext(); const { addMaterial } = materialStore(); const { getModelUuidByActionUuid, getPointUuidByActionUuid, getEventByModelUuid, getActionByUuid } = productStore(); @@ -15,14 +16,17 @@ export function useRetrieveHandler() { const { getAssetById, setCurrentAnimation } = assetStore(); const { selectedProduct } = selectedProductStore(); const { getArmBotById, addCurrentAction } = armBotStore(); + const { getMachineById } = machineStore(); const { isPlaying } = usePlayButtonStore(); const { speed } = useAnimationPlaySpeed(); const { isPaused } = usePauseButtonStore(); const { isReset } = useResetButtonStore(); + const { addHumanToMonitor } = useHumanEventManager(); const [activeRetrievals, setActiveRetrievals] = useState>(new Map()); const retrievalTimeRef = useRef>(new Map()); const retrievalCountRef = useRef>(new Map()); + const monitoredHumansRef = useRef>(new Set()); const [initialDelayComplete, setInitialDelayComplete] = useState(false); const delayTimerRef = useRef(null); @@ -312,9 +316,94 @@ export function useRetrieveHandler() { } if (human && !human.isScheduled && human.state === 'idle' && human.currentLoad < action.loadCapacity) { - if (humanAsset?.animationState?.current === 'idle') { - setCurrentAnimation(human.modelUuid, 'pickup', true, false, false); - } else if (humanAsset?.animationState?.current === 'pickup' && humanAsset.animationState.isCompleted) { + const triggeredModel = action.triggers[0]?.triggeredAsset?.triggeredModel?.modelUuid + ? getEventByModelUuid(selectedProduct.productUuid, action.triggers[0].triggeredAsset.triggeredModel.modelUuid) + : null; + if (triggeredModel?.type === 'vehicle') { + const model = getVehicleById(triggeredModel.modelUuid); + if (model && !model.isActive && model.state === 'idle' && model.isPicking && model.currentLoad < model.point.action.loadCapacity) { + if (humanAsset?.animationState?.current === 'idle') { + setCurrentAnimation(human.modelUuid, 'pickup', true, false, false); + } else if (humanAsset?.animationState?.current === 'pickup' && humanAsset.animationState.isCompleted) { + const lastMaterial = getLastMaterial(storageUnit.modelUuid); + if (lastMaterial) { + const material = createNewMaterial( + lastMaterial.materialId, + lastMaterial.materialType, + storageUnit.point.action + ); + if (material) { + removeLastMaterial(storageUnit.modelUuid); + updateCurrentLoad(storageUnit.modelUuid, -1); + incrementHumanLoad(human.modelUuid, 1); + addCurrentMaterialToHuman(human.modelUuid, material.materialType, material.materialId); + retrieveLogStatus(material.materialName, `is picked by ${human.modelName}`); + + retrievalCountRef.current.set(actionUuid, currentCount + 1); + } + } + } + return; + } + } else if (triggeredModel?.type === 'roboticArm') { + const armBot = getArmBotById(triggeredModel.modelUuid); + if (armBot && !armBot.isActive && armBot.state === 'idle' && !armBot.currentAction) { + if (humanAsset?.animationState?.current === 'idle') { + setCurrentAnimation(human.modelUuid, 'pickup', true, false, false); + } else if (humanAsset?.animationState?.current === 'pickup' && humanAsset.animationState.isCompleted) { + const lastMaterial = getLastMaterial(storageUnit.modelUuid); + if (lastMaterial) { + const material = createNewMaterial( + lastMaterial.materialId, + lastMaterial.materialType, + storageUnit.point.action + ); + if (material) { + removeLastMaterial(storageUnit.modelUuid); + updateCurrentLoad(storageUnit.modelUuid, -1); + incrementHumanLoad(human.modelUuid, 1); + addCurrentMaterialToHuman(human.modelUuid, material.materialType, material.materialId); + retrieveLogStatus(material.materialName, `is picked by ${human.modelName}`); + + retrievalCountRef.current.set(actionUuid, currentCount + 1); + } + } + } + return; + } + } else if (triggeredModel?.type === 'machine') { + const machine = getMachineById(triggeredModel.modelUuid); + if (machine && !machine.isActive && machine.state === 'idle' && !machine.currentAction) { + if (!monitoredHumansRef.current.has(human.modelUuid)) { + addHumanToMonitor(human.modelUuid, () => { + if (humanAsset?.animationState?.current === 'idle') { + setCurrentAnimation(human.modelUuid, 'pickup', true, false, false); + } + }, action.actionUuid); + } + monitoredHumansRef.current.add(human.modelUuid); + if (humanAsset?.animationState?.current === 'pickup' && humanAsset.animationState.isCompleted) { + const lastMaterial = getLastMaterial(storageUnit.modelUuid); + if (lastMaterial) { + const material = createNewMaterial( + lastMaterial.materialId, + lastMaterial.materialType, + storageUnit.point.action + ); + if (material) { + removeLastMaterial(storageUnit.modelUuid); + updateCurrentLoad(storageUnit.modelUuid, -1); + incrementHumanLoad(human.modelUuid, 1); + addCurrentMaterialToHuman(human.modelUuid, material.materialType, material.materialId); + retrieveLogStatus(material.materialName, `is picked by ${human.modelName}`); + retrievalCountRef.current.set(actionUuid, currentCount + 1); + } + } + monitoredHumansRef.current.delete(human.modelUuid); + } + return; + } + } else if (triggeredModel?.type === 'storageUnit') { const lastMaterial = getLastMaterial(storageUnit.modelUuid); if (lastMaterial) { const material = createNewMaterial( diff --git a/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx b/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx index f45f909..879c985 100644 --- a/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx +++ b/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx @@ -116,7 +116,7 @@ function MachineInstance({ machineDetail }: { readonly machineDetail: MachineSta function handleCallBack() { if (currentPhase == "processing") { setMachineState(machineDetail.modelUuid, 'idle'); - setMachineActive(machineDetail.modelUuid, false); + setMachineActive(machineDetail.modelUuid, true); setCurrentPhase("idle") isIncrememtable.current = true; machineStatus(machineDetail.modelUuid, "Machine has completed the processing") diff --git a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts index 4dc5ae2..04e7fa7 100644 --- a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts +++ b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts @@ -23,7 +23,7 @@ export function useTriggerHandler() { const { addHumanToMonitor } = useHumanEventManager(); const { getVehicleById } = vehicleStore(); const { getHumanById, setHumanScheduled } = humanStore(); - const { getMachineById } = machineStore(); + const { getMachineById, setMachineActive } = machineStore(); const { getStorageUnitById } = storageUnitStore(); const { getMaterialById, setCurrentLocation, setNextLocation, setPreviousLocation, setIsPaused, setIsVisible, setEndTime } = materialStore(); @@ -630,21 +630,24 @@ export function useTriggerHandler() { const previousModel = getEventByModelUuid(selectedProduct.productUuid, material.previous?.modelUuid || ''); if (previousModel) { if (previousModel.type === 'transfer' && previousModel.modelUuid === model.modelUuid) { + setMachineActive(trigger.triggerUuid, false); handleAction(action, materialId) } else { addConveyorToMonitor(conveyor.modelUuid, () => { - handleAction(action, materialId) + setMachineActive(trigger.triggerUuid, false); + handleAction(action, materialId); } ) } } else { - handleAction(action, materialId) + setMachineActive(trigger.triggerUuid, false); + handleAction(action, materialId); } - // handleAction(action, materialId) } } else { - handleAction(action, materialId) + setMachineActive(trigger.triggerUuid, false); + handleAction(action, materialId); } } else { @@ -660,21 +663,24 @@ export function useTriggerHandler() { const previousModel = getEventByModelUuid(selectedProduct.productUuid, material.previous?.modelUuid || ''); if (previousModel) { if (previousModel.type === 'transfer' && previousModel.modelUuid === model.modelUuid) { + setMachineActive(trigger.triggerUuid, false); handleAction(action, materialId) } else { addConveyorToMonitor(conveyor.modelUuid, () => { - handleAction(action, materialId) + setMachineActive(trigger.triggerUuid, false); + handleAction(action, materialId); } ) } } else { - handleAction(action, materialId) + setMachineActive(trigger.triggerUuid, false); + handleAction(action, materialId); } - // handleAction(action, materialId) } } else { - handleAction(action, materialId) + setMachineActive(trigger.triggerUuid, false); + handleAction(action, materialId); } } ); @@ -723,15 +729,18 @@ export function useTriggerHandler() { if (previousModel) { if (previousModel.type === 'transfer' && previousModel.modelUuid === model.modelUuid) { setHumanScheduled(human.modelUuid, true); + setMachineActive(trigger.triggerUuid, false); handleAction(action, materialId) } else { setHumanScheduled(human.modelUuid, true); addConveyorToMonitor(conveyor.modelUuid, () => { + setMachineActive(trigger.triggerUuid, false); handleAction(action, materialId) }) } } else { setHumanScheduled(human.modelUuid, true); + setMachineActive(trigger.triggerUuid, false); handleAction(action, materialId) } } @@ -742,6 +751,7 @@ export function useTriggerHandler() { // Handle current action from vehicle setIsPaused(materialId, true); setHumanScheduled(human.modelUuid, true); + setMachineActive(trigger.triggerUuid, false); handleAction(action, materialId); } else { @@ -750,12 +760,14 @@ export function useTriggerHandler() { setHumanScheduled(human.modelUuid, true); addVehicleToMonitor(vehicle.modelUuid, () => { + setMachineActive(trigger.triggerUuid, false); handleAction(action, materialId); }) } } } else { setHumanScheduled(human.modelUuid, true); + setMachineActive(trigger.triggerUuid, false); handleAction(action, materialId) } }, action.actionUuid); diff --git a/app/src/store/builder/useFloorStore.ts b/app/src/store/builder/useFloorStore.ts index efb431b..a045884 100644 --- a/app/src/store/builder/useFloorStore.ts +++ b/app/src/store/builder/useFloorStore.ts @@ -28,7 +28,7 @@ interface FloorStore { getFloorById: (uuid: string) => Floor | undefined; getFloorsByPointId: (uuid: string) => Floor[] | []; - getFloorByPoints: (points: Point[]) => Floor | undefined; + getFloorsByPoints: (points: [Point, Point]) => Floor[] | []; getFloorPointById: (uuid: string) => Point | undefined; getConnectedPoints: (uuid: string) => Point[]; } @@ -74,10 +74,13 @@ export const createFloorStore = () => { const updatedFloors: Floor[] = []; set(state => { + const newFloors: Floor[] = []; + for (const floor of state.floors) { const pointIndex = floor.points.findIndex(p => p.pointUuid === pointUuid); + if (pointIndex === -1) { - updatedFloors.push(JSON.parse(JSON.stringify(floor))); + newFloors.push(floor); continue; } @@ -87,11 +90,13 @@ export const createFloorStore = () => { removedFloors.push(JSON.parse(JSON.stringify(floor))); continue; } - floor.points = remainingPoints; - updatedFloors.push(JSON.parse(JSON.stringify(floor))); + + const updatedFloor = { ...floor, points: remainingPoints }; + updatedFloors.push(JSON.parse(JSON.stringify(updatedFloor))); + newFloors.push(updatedFloor); } - state.floors = updatedFloors; + state.floors = newFloors; }); return { removedFloors, updatedFloors }; @@ -102,6 +107,7 @@ export const createFloorStore = () => { const updatedFloors: Floor[] = []; set(state => { + const newFloors: Floor[] = []; for (const floor of state.floors) { const indices = floor.points.map((p, i) => ({ uuid: p.pointUuid, index: i })); @@ -110,7 +116,7 @@ export const createFloorStore = () => { const idxB = indices.find(i => i.uuid === pointB.pointUuid)?.index ?? -1; if (idxA === -1 || idxB === -1) { - updatedFloors.push(JSON.parse(JSON.stringify(floor))); + newFloors.push(floor); continue; } @@ -120,7 +126,7 @@ export const createFloorStore = () => { (idxB === 0 && idxA === floor.points.length - 1); if (!areAdjacent) { - updatedFloors.push(JSON.parse(JSON.stringify(floor))); + newFloors.push(floor); continue; } @@ -129,14 +135,15 @@ export const createFloorStore = () => { ); if (remainingPoints.length > 2) { - floor.points = remainingPoints; - updatedFloors.push(JSON.parse(JSON.stringify(floor))); + const updatedFloor = { ...floor, points: remainingPoints }; + updatedFloors.push(JSON.parse(JSON.stringify(updatedFloor))); + newFloors.push(updatedFloor); } else { removedFloors.push(JSON.parse(JSON.stringify(floor))); } } - state.floors = updatedFloors; + state.floors = newFloors; }); return { removedFloors, updatedFloors }; @@ -253,12 +260,32 @@ export const createFloorStore = () => { }); }, - getFloorByPoints: (points) => { - return get().floors.find(floor => { - const floorPointIds = new Set(floor.points.map(p => p.pointUuid)); - const givenPointIds = new Set(points.map(p => p.pointUuid)); - return floorPointIds.size === givenPointIds.size && [...floorPointIds].every(id => givenPointIds.has(id)); - }); + getFloorsByPoints: ([pointA, pointB]) => { + const Floors: Floor[] = []; + + for (const floor of get().floors) { + 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) { + continue; + } + + const areAdjacent = + Math.abs(idxA - idxB) === 1 || + (idxA === 0 && idxB === floor.points.length - 1) || + (idxB === 0 && idxA === floor.points.length - 1); + + if (!areAdjacent) { + continue; + } + + Floors.push(JSON.parse(JSON.stringify(floor))); + } + + return Floors; }, getFloorPointById: (pointUuid) => { diff --git a/app/src/store/builder/useUndoRedo2DStore.ts b/app/src/store/builder/useUndoRedo2DStore.ts new file mode 100644 index 0000000..9df0185 --- /dev/null +++ b/app/src/store/builder/useUndoRedo2DStore.ts @@ -0,0 +1,78 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; +import { undoRedoConfig } from '../../types/world/worldConstants'; + +type UndoRedo2DStore = { + undoStack: UndoRedo2DTypes[]; + redoStack: UndoRedo2DTypes[]; + + push2D: (entry: UndoRedo2DTypes) => void; + undo2D: () => UndoRedo2DTypes | undefined; + redo2D: () => UndoRedo2DTypes | undefined; + clearUndoRedo2D: () => void; + + peekUndo2D: () => UndoRedo2DTypes | undefined; + peekRedo2D: () => UndoRedo2DTypes | undefined; +}; + +export const createUndoRedo2DStore = () => { + return create()( + immer((set, get) => ({ + undoStack: [], + redoStack: [], + + push2D: (entry) => { + set((state) => { + state.undoStack.push(entry); + + if (state.undoStack.length > undoRedoConfig.undoRedoCount) { + state.undoStack.shift(); + } + + state.redoStack = []; + }); + }, + + undo2D: () => { + let lastAction: UndoRedo2DTypes | undefined; + set((state) => { + lastAction = state.undoStack.pop(); + if (lastAction) { + state.redoStack.unshift(lastAction); + } + }); + return lastAction; + }, + + redo2D: () => { + let redoAction: UndoRedo2DTypes | undefined; + set((state) => { + redoAction = state.redoStack.shift(); + if (redoAction) { + state.undoStack.push(redoAction); + } + }); + return redoAction; + }, + + clearUndoRedo2D: () => { + set((state) => { + state.undoStack = []; + state.redoStack = []; + }); + }, + + peekUndo2D: () => { + const stack = get().undoStack; + return stack.length > 0 ? stack[stack.length - 1] : undefined; + }, + + peekRedo2D: () => { + const stack = get().redoStack; + return stack.length > 0 ? stack[0] : undefined; + }, + })) + ) +} + +export type UndoRedo2DStoreType = ReturnType; \ No newline at end of file diff --git a/app/src/store/builder/useZoneStore.ts b/app/src/store/builder/useZoneStore.ts index 1a93e57..17134c7 100644 --- a/app/src/store/builder/useZoneStore.ts +++ b/app/src/store/builder/useZoneStore.ts @@ -21,7 +21,7 @@ interface ZoneStore { getZoneById: (uuid: string) => Zone | undefined; getZonesByPointId: (uuid: string) => Zone[] | []; - getZoneByPoints: (points: Point[]) => Zone | undefined; + getZonesByPoints: (points: Point[]) => Zone[] | []; getZonePointById: (uuid: string) => Point | undefined; getConnectedPoints: (uuid: string) => Point[]; } @@ -76,10 +76,13 @@ export const createZoneStore = () => { const updatedZones: Zone[] = []; set(state => { + const newZones: Zone[] = []; + for (const zone of state.zones) { const pointIndex = zone.points.findIndex(p => p.pointUuid === pointUuid); + if (pointIndex === -1) { - updatedZones.push(JSON.parse(JSON.stringify(zone))); + newZones.push(zone); continue; } @@ -89,11 +92,13 @@ export const createZoneStore = () => { removedZones.push(JSON.parse(JSON.stringify(zone))); continue; } - zone.points = remainingPoints; - updatedZones.push(JSON.parse(JSON.stringify(zone))); + + const updatedZone = { ...zone, points: remainingPoints }; + updatedZones.push(JSON.parse(JSON.stringify(updatedZone))); + newZones.push(updatedZone); } - state.zones = updatedZones; + state.zones = newZones; }); return { removedZones, updatedZones }; @@ -104,6 +109,7 @@ export const createZoneStore = () => { const updatedZones: Zone[] = []; set(state => { + const newZones: Zone[] = []; for (const zone of state.zones) { const indices = zone.points.map((p, i) => ({ uuid: p.pointUuid, index: i })); @@ -112,7 +118,7 @@ export const createZoneStore = () => { const idxB = indices.find(i => i.uuid === pointB.pointUuid)?.index ?? -1; if (idxA === -1 || idxB === -1) { - updatedZones.push(JSON.parse(JSON.stringify(zone))); + newZones.push(zone); continue; } @@ -122,7 +128,7 @@ export const createZoneStore = () => { (idxB === 0 && idxA === zone.points.length - 1); if (!areAdjacent) { - updatedZones.push(JSON.parse(JSON.stringify(zone))); + newZones.push(zone); continue; } @@ -131,14 +137,15 @@ export const createZoneStore = () => { ); if (remainingPoints.length > 2) { - zone.points = remainingPoints; - updatedZones.push(JSON.parse(JSON.stringify(zone))); + const updatedZone = { ...zone, points: remainingPoints }; + updatedZones.push(JSON.parse(JSON.stringify(updatedZone))); + newZones.push(updatedZone); } else { removedZones.push(JSON.parse(JSON.stringify(zone))); } } - state.zones = updatedZones; + state.zones = newZones; }); return { removedZones, updatedZones }; @@ -180,12 +187,32 @@ export const createZoneStore = () => { }); }, - getZoneByPoints: (points) => { - return get().zones.find(zone => { - const zonePointIds = new Set(zone.points.map(p => p.pointUuid)); - const givenPointIds = new Set(points.map(p => p.pointUuid)); - return zonePointIds.size === givenPointIds.size && [...zonePointIds].every(id => givenPointIds.has(id)); - }); + getZonesByPoints: ([pointA, pointB]) => { + const Zones: Zone[] = []; + + for (const zone of get().zones) { + const indices = zone.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) { + continue; + } + + const areAdjacent = + Math.abs(idxA - idxB) === 1 || + (idxA === 0 && idxB === zone.points.length - 1) || + (idxB === 0 && idxA === zone.points.length - 1); + + if (!areAdjacent) { + continue; + } + + Zones.push(JSON.parse(JSON.stringify(zone))); + } + + return Zones; }, getZonePointById: (pointUuid) => { diff --git a/app/src/types/builderTypes.d.ts b/app/src/types/builderTypes.d.ts index b48cd12..318050a 100644 --- a/app/src/types/builderTypes.d.ts +++ b/app/src/types/builderTypes.d.ts @@ -215,3 +215,41 @@ interface Aisle { } type Aisles = Aisle[]; + + +// Undo/Redo 2D + +type UndoRedo2DDataTypeSchema = + | { type: 'Wall'; lineData: Wall; newData?: Wall; timeStamp: string } + | { type: 'Floor'; lineData: Floor; newData?: Floor; timeStamp: string } + | { type: 'Zone'; lineData: Zone; newData?: Zone; timeStamp: string } + | { type: 'Aisle'; lineData: Aisle; newData?: Aisle; timeStamp: string }; + +type UndoRedo2DLineActionSchema = { + actionType: 'Line-Create' | 'Line-Update' | 'Line-Delete'; + point: UndoRedo2DDataTypeSchema; +} + +type UndoRedo2DLinesActionSchema = { + actionType: 'Lines-Create' | 'Lines-Update' | 'Lines-Delete'; + points: UndoRedo2DDataTypeSchema[]; +} + +type UndoRedo2DAction = UndoRedo2DLineActionSchema | UndoRedo2DLinesActionSchema; + +type UndoRedo2DDraw = { + type: 'Draw'; + actions: UndoRedo2DAction[]; +}; + +type UndoRedo2DUi = { + type: 'UI'; + action: any; // Define UI actions as needed +} + +type UndoRedo2DTypes = UndoRedo2DDraw | UndoRedo2DUi + +type UndoRedo2D = { + undoStack: UndoRedo2DTypes[]; + redoStack: UndoRedo2DTypes[]; +}; \ No newline at end of file diff --git a/app/src/types/world/worldConstants.ts b/app/src/types/world/worldConstants.ts index 0fe8f72..71d9caf 100644 --- a/app/src/types/world/worldConstants.ts +++ b/app/src/types/world/worldConstants.ts @@ -1,375 +1,383 @@ const savedTheme: string | null = localStorage.getItem("theme"); export type Controls = { - azimuthRotateSpeed: number; - polarRotateSpeed: number; - truckSpeed: number; - minDistance: number; - maxDistance: number; - maxPolarAngle: number; - leftMouse: number; - forwardSpeed: number; - backwardSpeed: number; - leftSpeed: number; - rightSpeed: number; + azimuthRotateSpeed: number; + polarRotateSpeed: number; + truckSpeed: number; + minDistance: number; + maxDistance: number; + maxPolarAngle: number; + leftMouse: number; + forwardSpeed: number; + backwardSpeed: number; + leftSpeed: number; + rightSpeed: number; }; export type ThirdPersonControls = { - azimuthRotateSpeed: number; - polarRotateSpeed: number; - truckSpeed: number; - maxDistance: number; - maxPolarAngle: number; - minZoom: number; - maxZoom: number; - targetOffset: number; - cameraHeight: number; - leftMouse: number; - rightMouse: number; - wheelMouse: number; - middleMouse: number; + azimuthRotateSpeed: number; + polarRotateSpeed: number; + truckSpeed: number; + maxDistance: number; + maxPolarAngle: number; + minZoom: number; + maxZoom: number; + targetOffset: number; + cameraHeight: number; + leftMouse: number; + rightMouse: number; + wheelMouse: number; + middleMouse: number; }; export type ControlsTransition = { - leftMouse: number; - rightMouse: number; - wheelMouse: number; - middleMouse: number; + leftMouse: number; + rightMouse: number; + wheelMouse: number; + middleMouse: number; }; export type TwoDimension = { - defaultPosition: [x: number, y: number, z: number]; - defaultTarget: [x: number, y: number, z: number]; - defaultAzimuth: number; - minDistance: number; - leftMouse: number; - rightMouse: number; + defaultPosition: [x: number, y: number, z: number]; + defaultTarget: [x: number, y: number, z: number]; + defaultAzimuth: number; + minDistance: number; + leftMouse: number; + rightMouse: number; }; export type ThreeDimension = { - defaultPosition: [x: number, y: number, z: number]; - defaultTarget: [x: number, y: number, z: number]; - defaultRotation: [x: number, y: number, z: number]; - defaultAzimuth: number; - boundaryBottom: [x: number, y: number, z: number]; - boundaryTop: [x: number, y: number, z: number]; - minDistance: number; - leftMouse: number; - rightMouse: number; + defaultPosition: [x: number, y: number, z: number]; + defaultTarget: [x: number, y: number, z: number]; + defaultRotation: [x: number, y: number, z: number]; + defaultAzimuth: number; + boundaryBottom: [x: number, y: number, z: number]; + boundaryTop: [x: number, y: number, z: number]; + minDistance: number; + leftMouse: number; + rightMouse: number; }; export type GridConfig = { - size: number; - divisions: number; - primaryColor: string; - secondaryColor: string; - position2D: [x: number, y: number, z: number]; - position3D: [x: number, y: number, z: number]; + size: number; + divisions: number; + primaryColor: string; + secondaryColor: string; + position2D: [x: number, y: number, z: number]; + position3D: [x: number, y: number, z: number]; }; export type PlaneConfig = { - position2D: [x: number, y: number, z: number]; - position3D: [x: number, y: number, z: number]; - rotation: number; - width: number; - height: number; - color: string; + position2D: [x: number, y: number, z: number]; + position3D: [x: number, y: number, z: number]; + rotation: number; + width: number; + height: number; + color: string; }; export type ShadowConfig = { - shadowOffset: number; - shadowmapSizewidth: number; - shadowmapSizeheight: number; - shadowcamerafar: number; - shadowcameranear: number; - shadowcameratop: number; - shadowcamerabottom: number; - shadowcameraleft: number; - shadowcameraright: number; - shadowbias: number; - shadownormalBias: number; - shadowMaterialPosition: [x: number, y: number, z: number]; - shadowMaterialRotation: [x: number, y: number, z: number]; - shadowMaterialOpacity: number; + shadowOffset: number; + shadowmapSizewidth: number; + shadowmapSizeheight: number; + shadowcamerafar: number; + shadowcameranear: number; + shadowcameratop: number; + shadowcamerabottom: number; + shadowcameraleft: number; + shadowcameraright: number; + shadowbias: number; + shadownormalBias: number; + shadowMaterialPosition: [x: number, y: number, z: number]; + shadowMaterialRotation: [x: number, y: number, z: number]; + shadowMaterialOpacity: number; }; export type SkyConfig = { - defaultTurbidity: number; - maxTurbidity: number; - minTurbidity: number; - defaultRayleigh: number; - mieCoefficient: number; - mieDirectionalG: number; - skyDistance: number; + defaultTurbidity: number; + maxTurbidity: number; + minTurbidity: number; + defaultRayleigh: number; + mieCoefficient: number; + mieDirectionalG: number; + skyDistance: number; }; export type AssetConfig = { - defaultScaleBeforeGsap: [number, number, number]; - defaultScaleAfterGsap: [number, number, number]; + defaultScaleBeforeGsap: [number, number, number]; + defaultScaleAfterGsap: [number, number, number]; }; export type PointConfig = { - defaultInnerColor: string; - defaultOuterColor: string; - deleteColor: string; - boxScale: [number, number, number]; - wallOuterColor: string; - floorOuterColor: string; - aisleOuterColor: string; - zoneOuterColor: string; - snappingThreshold: number; - helperColor: string; + defaultInnerColor: string; + defaultOuterColor: string; + deleteColor: string; + boxScale: [number, number, number]; + wallOuterColor: string; + floorOuterColor: string; + aisleOuterColor: string; + zoneOuterColor: string; + snappingThreshold: number; + helperColor: string; }; export type LineConfig = { - tubularSegments: number; - radius: number; - radialSegments: number; - wallName: string; - floorName: string; - aisleName: string; - zoneName: string; - referenceName: string; - lineIntersectionPoints: number; - defaultColor: string; - wallColor: string; - floorColor: string; - aisleColor: string; - zoneColor: string; - deleteColor: string; - helperColor: string; + tubularSegments: number; + radius: number; + radialSegments: number; + wallName: string; + floorName: string; + aisleName: string; + zoneName: string; + referenceName: string; + lineIntersectionPoints: number; + defaultColor: string; + wallColor: string; + floorColor: string; + aisleColor: string; + zoneColor: string; + deleteColor: string; + helperColor: string; }; export type WallConfig = { - defaultColor: string; - height: number; - width: number; + defaultColor: string; + height: number; + width: number; }; export type FloorConfig = { - defaultColor: string; - height: number; - textureScale: number; + defaultColor: string; + height: number; + textureScale: number; }; export type RoofConfig = { - defaultColor: string; - height: number; + defaultColor: string; + height: number; }; export type AisleConfig = { - width: number; - height: number; - defaultColor: string; + width: number; + height: number; + defaultColor: string; }; export type ZoneConfig = { - defaultColor: string; - height: number; - color: string; + defaultColor: string; + height: number; + color: string; }; export type ColumnConfig = { - defaultColor: string; + defaultColor: string; }; export type OutlineConfig = { - assetSelectColor: number; - assetDeleteColor: number; + assetSelectColor: number; + assetDeleteColor: number; }; export type DistanceConfig = { - minDistance: number; - maxDistance: number; + minDistance: number; + maxDistance: number; +}; + +export type undoRedoCount = { + undoRedoCount: number; }; export const firstPersonControls: Controls = { - azimuthRotateSpeed: 0.3, // Speed of rotation around the azimuth axis - polarRotateSpeed: 0.3, // Speed of rotation around the polar axis - truckSpeed: 10, // Speed of truck movement - minDistance: 0, // Minimum distance from the target - maxDistance: 0, // Maximum distance from the target - maxPolarAngle: Math.PI, // Maximum polar angle - leftMouse: 1, // Mouse button for rotation (ROTATE) - forwardSpeed: 0.1, // Speed of forward movement - backwardSpeed: -0.1, // Speed of backward movement - leftSpeed: -0.1, // Speed of left movement - rightSpeed: 0.1, // Speed of right movement + azimuthRotateSpeed: 0.3, // Speed of rotation around the azimuth axis + polarRotateSpeed: 0.3, // Speed of rotation around the polar axis + truckSpeed: 10, // Speed of truck movement + minDistance: 0, // Minimum distance from the target + maxDistance: 0, // Maximum distance from the target + maxPolarAngle: Math.PI, // Maximum polar angle + leftMouse: 1, // Mouse button for rotation (ROTATE) + forwardSpeed: 0.1, // Speed of forward movement + backwardSpeed: -0.1, // Speed of backward movement + leftSpeed: -0.1, // Speed of left movement + rightSpeed: 0.1, // Speed of right movement }; export const thirdPersonControls: ThirdPersonControls = { - azimuthRotateSpeed: 1, // Speed of rotation around the azimuth axis - polarRotateSpeed: 1, // Speed of rotation around the polar axis - truckSpeed: 2, // Speed of truck movement - maxDistance: 100, // Maximum distance from the target - maxPolarAngle: Math.PI / 2 - 0.05, // Maximum polar angle - minZoom: 6, // Minimum zoom level - maxZoom: 100, // Maximum zoom level - targetOffset: 20, // Offset of the target from the camera - cameraHeight: 30, // Height of the camera - leftMouse: 2, // Mouse button for panning - rightMouse: 1, // Mouse button for rotation - wheelMouse: 8, // Mouse button for zooming - middleMouse: 8, // Mouse button for zooming + azimuthRotateSpeed: 1, // Speed of rotation around the azimuth axis + polarRotateSpeed: 1, // Speed of rotation around the polar axis + truckSpeed: 2, // Speed of truck movement + maxDistance: 100, // Maximum distance from the target + maxPolarAngle: Math.PI / 2 - 0.05, // Maximum polar angle + minZoom: 6, // Minimum zoom level + maxZoom: 100, // Maximum zoom level + targetOffset: 20, // Offset of the target from the camera + cameraHeight: 30, // Height of the camera + leftMouse: 2, // Mouse button for panning + rightMouse: 1, // Mouse button for rotation + wheelMouse: 8, // Mouse button for zooming + middleMouse: 8, // Mouse button for zooming }; export const controlsTransition: ControlsTransition = { - leftMouse: 0, // Mouse button for no action - rightMouse: 0, // Mouse button for no action - wheelMouse: 0, // Mouse button for no action - middleMouse: 0, // Mouse button for no action + leftMouse: 0, // Mouse button for no action + rightMouse: 0, // Mouse button for no action + wheelMouse: 0, // Mouse button for no action + middleMouse: 0, // Mouse button for no action }; export const twoDimension: TwoDimension = { - defaultPosition: [0, 100, 0], // Default position of the camera - defaultTarget: [0, 0, 0], // Default target of the camera - defaultAzimuth: 0, // Default azimuth of the camera - minDistance: 25, // Minimum distance from the target - leftMouse: 2, // Mouse button for panning - rightMouse: 0, // Mouse button for no action + defaultPosition: [0, 100, 0], // Default position of the camera + defaultTarget: [0, 0, 0], // Default target of the camera + defaultAzimuth: 0, // Default azimuth of the camera + minDistance: 25, // Minimum distance from the target + leftMouse: 2, // Mouse button for panning + rightMouse: 0, // Mouse button for no action }; export const camPositionUpdateInterval: number = 200; // Interval for updating the camera position export const gridConfig: GridConfig = { - size: 150, // Size of the grid - divisions: 75, // Number of divisions in the grid - primaryColor: savedTheme === "dark" ? "#131313" : "#d5d5d5", // Primary color of the grid - secondaryColor: savedTheme === "dark" ? "#434343" : "#e3e3e3", // Secondary color of the grid + size: 150, // Size of the grid + divisions: 75, // Number of divisions in the grid + primaryColor: savedTheme === "dark" ? "#131313" : "#d5d5d5", // Primary color of the grid + secondaryColor: savedTheme === "dark" ? "#434343" : "#e3e3e3", // Secondary color of the grid - position2D: [0, 0.1, 0], // Position of the grid in 2D view - position3D: [0, -0.5, 0], // Position of the grid in 3D view + position2D: [0, 0.1, 0], // Position of the grid in 2D view + position3D: [0, -0.5, 0], // Position of the grid in 3D view }; export const threeDimension: ThreeDimension = { - defaultPosition: [0, 40, 30], // Default position of the camera - defaultTarget: [0, 0, 0], // Default target of the camera - defaultRotation: [0, 0, 0], // Default rotation of the camera - defaultAzimuth: 0, // Default azimuth of the camera - boundaryBottom: [-gridConfig.size / 2, 0, -gridConfig.size / 2], // Bottom boundary of the camera movement - boundaryTop: [gridConfig.size / 2, 100, gridConfig.size / 2], // Top boundary of the camera movement - minDistance: 1, // Minimum distance from the target - leftMouse: 2, // Mouse button for panning - rightMouse: 1, // Mouse button for rotation + defaultPosition: [0, 40, 30], // Default position of the camera + defaultTarget: [0, 0, 0], // Default target of the camera + defaultRotation: [0, 0, 0], // Default rotation of the camera + defaultAzimuth: 0, // Default azimuth of the camera + boundaryBottom: [-gridConfig.size / 2, 0, -gridConfig.size / 2], // Bottom boundary of the camera movement + boundaryTop: [gridConfig.size / 2, 100, gridConfig.size / 2], // Top boundary of the camera movement + minDistance: 1, // Minimum distance from the target + leftMouse: 2, // Mouse button for panning + rightMouse: 1, // Mouse button for rotation }; export const planeConfig: PlaneConfig = { - position2D: [0, -0.5, 0], // Position of the plane - position3D: [0, -0.65, 0], // Position of the plane - rotation: -Math.PI / 2, // Rotation of the plane + position2D: [0, -0.5, 0], // Position of the plane + position3D: [0, -0.65, 0], // Position of the plane + rotation: -Math.PI / 2, // Rotation of the plane - width: 150, // Width of the plane - height: 150, // Height of the plane - color: savedTheme === "dark" ? "#323232" : "#f3f3f3", // Color of the plane + width: 150, // Width of the plane + height: 150, // Height of the plane + color: savedTheme === "dark" ? "#323232" : "#f3f3f3", // Color of the plane }; export const shadowConfig: ShadowConfig = { - shadowOffset: 50, // Offset of the shadow - // shadowmapSizewidth: 1024, // Width of the shadow map - // shadowmapSizeheight: 1024, // Height of the shadow map - shadowmapSizewidth: 2048, // Width of the shadow map - shadowmapSizeheight: 2048, // Height of the shadow map - // shadowmapSizewidth: 8192, // Width of the shadow map - // shadowmapSizeheight: 8192, // Height of the shadow map - shadowcamerafar: 70, // Far plane of the shadow camera - shadowcameranear: 0.1, // Near plane of the shadow camera - shadowcameratop: 30, // Top plane of the shadow camera - shadowcamerabottom: -30, // Bottom plane of the shadow camera - shadowcameraleft: -30, // Left plane of the shadow camera - shadowcameraright: 30, // Right plane of the shadow camera - shadowbias: -0.001, // Bias of the shadow - shadownormalBias: 0.02, // Normal bias of the shadow - shadowMaterialPosition: [0, 0.01, 0], // Position of the shadow material - shadowMaterialRotation: [-Math.PI / 2, 0, 0], // Rotation of the shadow material - shadowMaterialOpacity: 0.1, // Opacity of the shadow material + shadowOffset: 50, // Offset of the shadow + // shadowmapSizewidth: 1024, // Width of the shadow map + // shadowmapSizeheight: 1024, // Height of the shadow map + shadowmapSizewidth: 2048, // Width of the shadow map + shadowmapSizeheight: 2048, // Height of the shadow map + // shadowmapSizewidth: 8192, // Width of the shadow map + // shadowmapSizeheight: 8192, // Height of the shadow map + shadowcamerafar: 70, // Far plane of the shadow camera + shadowcameranear: 0.1, // Near plane of the shadow camera + shadowcameratop: 30, // Top plane of the shadow camera + shadowcamerabottom: -30, // Bottom plane of the shadow camera + shadowcameraleft: -30, // Left plane of the shadow camera + shadowcameraright: 30, // Right plane of the shadow camera + shadowbias: -0.001, // Bias of the shadow + shadownormalBias: 0.02, // Normal bias of the shadow + shadowMaterialPosition: [0, 0.01, 0], // Position of the shadow material + shadowMaterialRotation: [-Math.PI / 2, 0, 0], // Rotation of the shadow material + shadowMaterialOpacity: 0.1, // Opacity of the shadow material }; export const skyConfig: SkyConfig = { - defaultTurbidity: 10.0, // Default turbidity of the sky - maxTurbidity: 20.0, // Maximum turbidity of the sky - minTurbidity: 0.0, // Minimum turbidity of the sky - defaultRayleigh: 1.9, // Default Rayleigh scattering coefficient - mieCoefficient: 0.1, // Mie scattering coefficient - mieDirectionalG: 1.0, // Mie directional G - skyDistance: 2000, // Distance of the sky + defaultTurbidity: 10.0, // Default turbidity of the sky + maxTurbidity: 20.0, // Maximum turbidity of the sky + minTurbidity: 0.0, // Minimum turbidity of the sky + defaultRayleigh: 1.9, // Default Rayleigh scattering coefficient + mieCoefficient: 0.1, // Mie scattering coefficient + mieDirectionalG: 1.0, // Mie directional G + skyDistance: 2000, // Distance of the sky }; export const assetConfig: AssetConfig = { - defaultScaleBeforeGsap: [0.1, 0.1, 0.1], // Default scale of the assets - defaultScaleAfterGsap: [1, 1, 1], // Default scale of the assets + defaultScaleBeforeGsap: [0.1, 0.1, 0.1], // Default scale of the assets + defaultScaleAfterGsap: [1, 1, 1], // Default scale of the assets }; export const pointConfig: PointConfig = { - defaultInnerColor: "#ffffff", // Default inner color of the points - defaultOuterColor: "#ffffff", // Default outer color of the points - deleteColor: "#ff0000", // Color of the points when deleting - boxScale: [0.5, 0.5, 0.5], // Scale of the points - wallOuterColor: "#C7C7C7", // Outer color of the wall points - floorOuterColor: "#808080", // Outer color of the floor points - aisleOuterColor: "#FBBC05", // Outer color of the aisle points - zoneOuterColor: "#007BFF", // Outer color of the zone points - snappingThreshold: 1, // Threshold for snapping - helperColor: "#C164FF", // Color of the helper lines + defaultInnerColor: "#ffffff", // Default inner color of the points + defaultOuterColor: "#ffffff", // Default outer color of the points + deleteColor: "#ff0000", // Color of the points when deleting + boxScale: [0.5, 0.5, 0.5], // Scale of the points + wallOuterColor: "#C7C7C7", // Outer color of the wall points + floorOuterColor: "#808080", // Outer color of the floor points + aisleOuterColor: "#FBBC05", // Outer color of the aisle points + zoneOuterColor: "#007BFF", // Outer color of the zone points + snappingThreshold: 1, // Threshold for snapping + helperColor: "#C164FF", // Color of the helper lines }; export const lineConfig: LineConfig = { - tubularSegments: 64, // Number of tubular segments - radius: 0.15, // Radius of the lines - radialSegments: 8, // Number of radial segments - wallName: "WallLine", // Name of the wall lines - floorName: "FloorLine", // Name of the floor lines - aisleName: "AisleLine", // Name of the aisle lines - zoneName: "ZoneLine", // Name of the zone lines - referenceName: "ReferenceLine", // Name of the reference lines - lineIntersectionPoints: 300, // Number of intersection points - defaultColor: "#000000", // Default color of the lines - wallColor: "#C7C7C7", // Color of the wall lines - floorColor: "#808080", // Color of the floor lines - aisleColor: "#FBBC05", // Color of the aisle lines - zoneColor: "#007BFF", // Color of the zone lines - deleteColor: "#ff0000", // Color of the line when deleting - helperColor: "#C164FF", // Color of the helper lines + tubularSegments: 64, // Number of tubular segments + radius: 0.15, // Radius of the lines + radialSegments: 8, // Number of radial segments + wallName: "WallLine", // Name of the wall lines + floorName: "FloorLine", // Name of the floor lines + aisleName: "AisleLine", // Name of the aisle lines + zoneName: "ZoneLine", // Name of the zone lines + referenceName: "ReferenceLine", // Name of the reference lines + lineIntersectionPoints: 300, // Number of intersection points + defaultColor: "#000000", // Default color of the lines + wallColor: "#C7C7C7", // Color of the wall lines + floorColor: "#808080", // Color of the floor lines + aisleColor: "#FBBC05", // Color of the aisle lines + zoneColor: "#007BFF", // Color of the zone lines + deleteColor: "#ff0000", // Color of the line when deleting + helperColor: "#C164FF", // Color of the helper lines }; export const wallConfig: WallConfig = { - defaultColor: "#f2f2f2", // Default color of the walls - height: 7.5, // Height of the walls - width: 0.05, // Width of the walls + defaultColor: "#f2f2f2", // Default color of the walls + height: 7.5, // Height of the walls + width: 0.05, // Width of the walls }; export const floorConfig: FloorConfig = { - defaultColor: "grey", // Default color of the floors - height: 0.1, // Height of the floors - textureScale: 1, // Scale of the floor texture + defaultColor: "grey", // Default color of the floors + height: 0.1, // Height of the floors + textureScale: 1, // Scale of the floor texture }; export const roofConfig: RoofConfig = { - defaultColor: "grey", // Default color of the roofs - height: 0.1, // Height of the roofs + defaultColor: "grey", // Default color of the roofs + height: 0.1, // Height of the roofs }; export const aisleConfig: AisleConfig = { - width: 0.1, // Width of the aisles - height: 0.01, // Height of the aisles - defaultColor: '#E2AC09', // Default color of the aisles + width: 0.1, // Width of the aisles + height: 0.01, // Height of the aisles + defaultColor: '#E2AC09', // Default color of the aisles }; export const zoneConfig: ZoneConfig = { - defaultColor: "black", // Default color of the zones - height: 3, - color: "#8656DF", // Color of the zones + defaultColor: "black", // Default color of the zones + height: 3, + color: "#8656DF", // Color of the zones }; export const columnConfig: ColumnConfig = { - defaultColor: "White", // Default color of the columns + defaultColor: "White", // Default color of the columns }; export const outlineConfig: OutlineConfig = { - assetSelectColor: 0x0054fe, // Color of the selected assets - assetDeleteColor: 0xff0000, // Color of the deleted assets + assetSelectColor: 0x0054fe, // Color of the selected assets + assetDeleteColor: 0xff0000, // Color of the deleted assets }; export const distanceConfig: DistanceConfig = { - minDistance: 20, - maxDistance: 75, + minDistance: 20, + maxDistance: 75, }; + +export const undoRedoConfig: undoRedoCount = { + undoRedoCount: 50, +} \ No newline at end of file