diff --git a/app/src/modules/builder/asset/models/model/eventHandlers/useEventHandlers.ts b/app/src/modules/builder/asset/models/model/eventHandlers/useEventHandlers.ts index a0ea33a..1fc26ae 100644 --- a/app/src/modules/builder/asset/models/model/eventHandlers/useEventHandlers.ts +++ b/app/src/modules/builder/asset/models/model/eventHandlers/useEventHandlers.ts @@ -30,11 +30,11 @@ export function useModelEventHandlers({ const { toggleView } = useToggleView(); const { subModule } = useSubModuleStore(); const { socket } = useSocketStore(); - const { eventStore, productStore, assetStore } = useSceneContext(); + const { eventStore, productStore, assetStore, undoRedo3DStore } = useSceneContext(); + const { push3D } = undoRedo3DStore(); const { removeAsset } = assetStore(); - const { removeEvent } = eventStore(); + const { removeEvent, getEventByModelUuid } = eventStore(); const { getIsEventInProduct, addPoint, deleteEvent } = productStore(); - const { getEventByModelUuid } = eventStore(); const { setSelectedAsset, clearSelectedAsset } = useSelectedAsset(); const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem(); const { setSelectedFloorItem } = useSelectedFloorItem(); @@ -152,6 +152,21 @@ export function useModelEventHandlers({ removeAsset(asset.modelUuid); + push3D({ + type: 'Scene', + actions: [ + { + module: "builder", + actionType: "Asset-Delete", + asset: { + type: "Asset", + assetData: asset, + timeStap: new Date().toISOString() + } + } + ] + }); + echo.success("Model Removed!"); } diff --git a/app/src/modules/builder/asset/models/model/model.tsx b/app/src/modules/builder/asset/models/model/model.tsx index 9767138..3f128bb 100644 --- a/app/src/modules/builder/asset/models/model/model.tsx +++ b/app/src/modules/builder/asset/models/model/model.tsx @@ -8,7 +8,7 @@ import useModuleStore from '../../../../../store/useModuleStore'; import { useSceneContext } from '../../../../scene/sceneContext'; import { SkeletonUtils } from 'three-stdlib'; -import { getAssetIksApi } from '../../../../../services/simulation/ik/getAssetIKs'; +import { getAssetFieldApi } from '../../../../../services/factoryBuilder/asset/floorAsset/getAssetField'; import { ModelAnimator } from './animator/modelAnimator'; import { useModelEventHandlers } from './eventHandlers/useEventHandlers'; @@ -26,19 +26,31 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere const [boundingBox, setBoundingBox] = useState(null); const [isSelected, setIsSelected] = useState(false); const groupRef = useRef(null); - const [ikData, setIkData] = useState(); + const [fieldData, setFieldData] = useState(); const { selectedAssets } = useSelectedAssets(); useEffect(() => { - if (!ikData && asset.eventData && asset.eventData.type === 'ArmBot') { - getAssetIksApi(asset.assetId).then((data) => { - if (data.iks) { - const iks: IK[] = data.iks; - setIkData(iks); + if (!fieldData && asset.eventData) { + getAssetFieldApi(asset.assetId).then((data) => { + if (data.type === 'ArmBot') { + if (data.data) { + const fieldData: IK[] = data.data; + setFieldData(fieldData); + } + } else if (data.type === 'Conveyor') { + if (data.data) { + const fieldData = data.data; + setFieldData(fieldData); + } + } else if (data.type === 'Crane') { + if (data.data) { + const fieldData = data.data; + setFieldData(fieldData); + } } }) } - }, [asset.modelUuid, ikData]) + }, [asset.modelUuid, fieldData]) useEffect(() => { setDeletableFloorItem(null); @@ -157,7 +169,7 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere position={asset.position} rotation={asset.rotation} visible={asset.isVisible} - userData={{ ...asset, iks: ikData }} + userData={{ ...asset, fieldData: fieldData }} castShadow receiveShadow onDoubleClick={(e) => { diff --git a/app/src/modules/scene/controls/controls.tsx b/app/src/modules/scene/controls/controls.tsx index 79b9c9f..d58c362 100644 --- a/app/src/modules/scene/controls/controls.tsx +++ b/app/src/modules/scene/controls/controls.tsx @@ -16,6 +16,7 @@ import { getUserData } from "../../../functions/getUserData"; import SelectionControls2D from "./selectionControls/selection2D/selectionControls2D"; import UndoRedo2DControls from "./undoRedoControls/undoRedo2D/undoRedo2DControls"; +import UndoRedo3DControls from "./undoRedoControls/undoRedo3D/undoRedo3DControls"; export default function Controls() { const controlsRef = useRef(null); @@ -144,6 +145,8 @@ export default function Controls() { + + diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/copyPasteControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/copyPasteControls3D.tsx index e28da3c..e9d7bc3 100644 --- a/app/src/modules/scene/controls/selectionControls/selection3D/copyPasteControls3D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection3D/copyPasteControls3D.tsx @@ -28,7 +28,8 @@ const CopyPasteControls3D = ({ const { selectedAssets, setSelectedAssets } = useSelectedAssets(); const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); const { socket } = useSocketStore(); - const { assetStore, eventStore } = useSceneContext(); + const { assetStore, eventStore, undoRedo3DStore } = useSceneContext(); + const { push3D } = undoRedo3DStore(); const { addEvent } = eventStore(); const { projectId } = useParams(); const { assets, addAsset, updateAsset, removeAsset, getAssetById } = assetStore(); @@ -198,6 +199,9 @@ const CopyPasteControls3D = ({ const addPastedObjects = () => { if (pastedObjects.length === 0) return; + const undoActions: UndoRedo3DAction[] = []; + const assetsToCopy: AssetData[] = []; + pastedObjects.forEach(async (pastedAsset: THREE.Object3D) => { if (pastedAsset) { const assetUuid = pastedAsset.userData.modelUuid; @@ -529,9 +533,45 @@ const CopyPasteControls3D = ({ updateAsset(asset.modelUuid, asset); } + + assetsToCopy.push({ + type: "Asset", + assetData: { + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, + assetId: newFloorItem.assetId, + position: [position.x, position.y, position.z], + rotation: [pastedAsset.rotation.x, pastedAsset.rotation.y, pastedAsset.rotation.z], + isLocked: false, + isVisible: true, + isCollidable: false, + opacity: 1, + eventData: newFloorItem.eventData || undefined + }, + timeStap: new Date().toISOString() + }); } }); + if (assetsToCopy.length === 1) { + undoActions.push({ + module: "builder", + actionType: "Asset-Copied", + asset: assetsToCopy[0] + }); + } else { + undoActions.push({ + module: "builder", + actionType: "Assets-Copied", + assets: assetsToCopy + }); + } + + push3D({ + type: 'Scene', + actions: undoActions + }); + echo.success("Object added!"); clearSelection(); }; diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/duplicationControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/duplicationControls3D.tsx index f494025..344ae4e 100644 --- a/app/src/modules/scene/controls/selectionControls/selection3D/duplicationControls3D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection3D/duplicationControls3D.tsx @@ -26,7 +26,8 @@ const DuplicationControls3D = ({ const { selectedAssets, setSelectedAssets } = useSelectedAssets(); const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []); const { socket } = useSocketStore(); - const { assetStore, eventStore } = useSceneContext(); + const { assetStore, eventStore, undoRedo3DStore } = useSceneContext(); + const { push3D } = undoRedo3DStore(); const { addEvent } = eventStore(); const { projectId } = useParams(); const { assets, addAsset, updateAsset, removeAsset, getAssetById } = assetStore(); @@ -199,6 +200,9 @@ const DuplicationControls3D = ({ const addDuplicatedAssets = () => { if (duplicatedObjects.length === 0) return; + const undoActions: UndoRedo3DAction[] = []; + const assetsToDuplicate: AssetData[] = []; + duplicatedObjects.forEach(async (duplicatedAsset: THREE.Object3D) => { if (duplicatedAsset) { const assetUuid = duplicatedAsset.userData.modelUuid; @@ -530,9 +534,45 @@ const DuplicationControls3D = ({ updateAsset(asset.modelUuid, asset); } + + assetsToDuplicate.push({ + type: "Asset", + assetData: { + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, + assetId: newFloorItem.assetId, + position: [position.x, position.y, position.z], + rotation: [duplicatedAsset.rotation.x, duplicatedAsset.rotation.y, duplicatedAsset.rotation.z], + isLocked: false, + isVisible: true, + isCollidable: false, + opacity: 1, + eventData: newFloorItem.eventData || undefined + }, + timeStap: new Date().toISOString() + }); } }); + if (assetsToDuplicate.length === 1) { + undoActions.push({ + module: "builder", + actionType: "Asset-Copied", + asset: assetsToDuplicate[0] + }); + } else { + undoActions.push({ + module: "builder", + actionType: "Assets-Copied", + assets: assetsToDuplicate + }); + } + + push3D({ + type: 'Scene', + actions: undoActions + }); + echo.success("Object duplicated!"); clearSelection(); }; diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx index 6f65300..4916886 100644 --- a/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx @@ -37,7 +37,8 @@ function MoveControls3D({ const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">(""); const { userId, organization } = getUserData(); const { projectId } = useParams(); - const { assetStore, eventStore, productStore } = useSceneContext(); + const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext(); + const { push3D } = undoRedo3DStore(); const { updateAsset, getAssetById } = assetStore(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); @@ -284,6 +285,9 @@ function MoveControls3D({ const placeMovedAssets = () => { if (movedObjects.length === 0) return; + const undoActions: UndoRedo3DAction[] = []; + const assetsToUpdate: AssetData[] = []; + movedObjects.forEach(async (movedAsset: THREE.Object3D) => { if (movedAsset) { const assetUuid = movedAsset.userData.modelUuid; @@ -291,6 +295,32 @@ function MoveControls3D({ const model = scene.getObjectByProperty("uuid", movedAsset.userData.modelUuid); if (!asset || !model) return; const position = new THREE.Vector3().copy(model.position); + const initialState = initialStates[movedAsset.uuid]; + + if (initialState) { + assetsToUpdate.push({ + type: "Asset", + assetData: { + ...asset, + position: [ + initialState.position.x, + initialState.position.y, + initialState.position.z + ], + rotation: [ + initialState.rotation?.x || 0, + initialState.rotation?.y || 0, + initialState.rotation?.z || 0 + ] + }, + newData: { + ...asset, + position: [position.x, position.y, position.z], + rotation: [movedAsset.rotation.x, movedAsset.rotation.y, movedAsset.rotation.z] + }, + timeStap: new Date().toISOString() + }); + } const newFloorItem: Types.FloorItemType = { modelUuid: movedAsset.userData.modelUuid, @@ -368,6 +398,27 @@ function MoveControls3D({ } }); + if (assetsToUpdate.length > 0) { + if (assetsToUpdate.length === 1) { + undoActions.push({ + module: "builder", + actionType: "Asset-Update", + asset: assetsToUpdate[0] + }); + } else { + undoActions.push({ + module: "builder", + actionType: "Assets-Update", + assets: assetsToUpdate + }); + } + + push3D({ + type: 'Scene', + actions: undoActions + }); + } + echo.success("Object moved!"); setIsMoving(false); clearSelection(); diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx index 0ca114d..bd5e254 100644 --- a/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection3D/rotateControls3D.tsx @@ -32,7 +32,8 @@ function RotateControls3D({ const { socket } = useSocketStore(); const { userId, organization } = getUserData(); const { projectId } = useParams(); - const { assetStore, eventStore, productStore } = useSceneContext(); + const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext(); + const { push3D } = undoRedo3DStore(); const { updateAsset } = assetStore(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); @@ -222,12 +223,43 @@ function RotateControls3D({ const placeRotatedAssets = useCallback(() => { if (rotatedObjects.length === 0) return; + const undoActions: UndoRedo3DAction[] = []; + const assetsToUpdate: AssetData[] = []; + rotatedObjects.forEach((obj: THREE.Object3D) => { if (obj && obj.userData.modelUuid) { + const asset = assetStore.getState().getAssetById(obj.userData.modelUuid); + if (!asset) return; + const rotationArray: [number, number, number] = [obj.rotation.x, obj.rotation.y, obj.rotation.z]; const positionArray: [number, number, number] = [obj.position.x, obj.position.y, obj.position.z]; + if (initialRotations[obj.uuid] && initialPositions[obj.uuid]) { + assetsToUpdate.push({ + type: "Asset", + assetData: { + ...asset, + position: [ + initialPositions[obj.uuid].x, + initialPositions[obj.uuid].y, + initialPositions[obj.uuid].z + ], + rotation: [ + initialRotations[obj.uuid].x, + initialRotations[obj.uuid].y, + initialRotations[obj.uuid].z + ] + }, + newData: { + ...asset, + position: positionArray, + rotation: rotationArray + }, + timeStap: new Date().toISOString() + }); + } + const newFloorItem: Types.FloorItemType = { modelUuid: obj.userData.modelUuid, modelName: obj.userData.modelName, @@ -305,6 +337,27 @@ function RotateControls3D({ } }); + if (assetsToUpdate.length > 0) { + if (assetsToUpdate.length === 1) { + undoActions.push({ + module: "builder", + actionType: "Asset-Update", + asset: assetsToUpdate[0] + }); + } else { + undoActions.push({ + module: "builder", + actionType: "Assets-Update", + assets: assetsToUpdate + }); + } + + push3D({ + type: 'Scene', + actions: undoActions + }); + } + setIsRotating(false); clearSelection(); }, [rotatedObjects, eventStore, productStore, selectedProduct, updateBackend, projectId, updateAsset, organization, socket, selectedVersion, userId]); diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/selectionControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/selectionControls3D.tsx index c72da8b..91acd3c 100644 --- a/app/src/modules/scene/controls/selectionControls/selection3D/selectionControls3D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection3D/selectionControls3D.tsx @@ -31,8 +31,9 @@ const SelectionControls3D: React.FC = () => { const boundingBoxRef = useRef(); const { activeModule } = useModuleStore(); const { socket } = useSocketStore(); - const { assetStore, eventStore, productStore } = useSceneContext(); - const { removeAsset } = assetStore(); + const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext(); + const { push3D } = undoRedo3DStore(); + const { removeAsset, getAssetById } = assetStore(); const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]); const { toolMode } = useToolMode(); const { selectedVersionStore } = useVersionContext(); @@ -266,12 +267,18 @@ const SelectionControls3D: React.FC = () => { const deleteSelection = () => { if (selectedAssets.length > 0 && duplicatedObjects.length === 0) { + const undoActions: UndoRedo3DAction[] = []; + const assetsToDelete: AssetData[] = []; + const selectedUUIDs = selectedAssets.map((mesh: THREE.Object3D) => mesh.uuid); selectedAssets.forEach((selectedMesh: THREE.Object3D) => { + const asset = getAssetById(selectedMesh.userData.modelUuid); + if (!asset) return; + //REST - // const response = await deleteFloorItem(organization, selectedMesh.userData.modelUuid, selectedMesh.userData.modelName); + // const response = deleteFloorItem(organization, selectedMesh.userData.modelUuid, selectedMesh.userData.modelName); //SOCKET @@ -321,6 +328,31 @@ const SelectionControls3D: React.FC = () => { } }); + + assetsToDelete.push({ + type: "Asset", + assetData: asset, + timeStap: new Date().toISOString() + }); + }); + + if (assetsToDelete.length === 1) { + undoActions.push({ + module: "builder", + actionType: "Asset-Delete", + asset: assetsToDelete[0] + }); + } else { + undoActions.push({ + module: "builder", + actionType: "Assets-Delete", + assets: assetsToDelete + }); + } + + push3D({ + type: 'Scene', + actions: undoActions }); selectedUUIDs.forEach((uuid: string) => { diff --git a/app/src/modules/scene/controls/transformControls/transformControls.tsx b/app/src/modules/scene/controls/transformControls/transformControls.tsx index 6d5e7d5..af51542 100644 --- a/app/src/modules/scene/controls/transformControls/transformControls.tsx +++ b/app/src/modules/scene/controls/transformControls/transformControls.tsx @@ -24,7 +24,8 @@ export default function TransformControl() { const { socket } = useSocketStore(); const { selectedProductStore } = useProductContext(); const { selectedProduct } = selectedProductStore(); - const { assetStore, eventStore, productStore } = useSceneContext(); + const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext(); + const { push3D } = undoRedo3DStore(); const { updateAsset, getAssetById } = assetStore(); const { userId, organization } = getUserData(); const { selectedVersionStore } = useVersionContext(); @@ -137,13 +138,37 @@ export default function TransformControl() { projectId }; - // console.log('data: ', data); socket.emit("v1:model-asset:add", data); + + push3D({ + type: 'Scene', + actions: [ + { + module: "builder", + actionType: "Asset-Update", + asset: { + type: "Asset", + assetData: asset, + newData: { + ...asset, + position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z], + rotation: [selectedFloorItem.rotation.x, selectedFloorItem.rotation.y, selectedFloorItem.rotation.z], + }, + timeStap: new Date().toISOString() + } + } + ] + }); } } useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { + const isTextInput = (element: Element | null): boolean => + element instanceof HTMLInputElement || + element instanceof HTMLTextAreaElement || + element?.getAttribute("contenteditable") === "true"; + if (isTextInput(document.activeElement)) return; const keyCombination = detectModifierKeys(e); if (!selectedFloorItem) return; if (keyCombination === "G") { diff --git a/app/src/modules/scene/controls/undoRedoControls/handlers/useRedoHandler.ts b/app/src/modules/scene/controls/undoRedoControls/handlers/use2DRedoHandler.ts similarity index 99% rename from app/src/modules/scene/controls/undoRedoControls/handlers/useRedoHandler.ts rename to app/src/modules/scene/controls/undoRedoControls/handlers/use2DRedoHandler.ts index 99902c1..12a0929 100644 --- a/app/src/modules/scene/controls/undoRedoControls/handlers/useRedoHandler.ts +++ b/app/src/modules/scene/controls/undoRedoControls/handlers/use2DRedoHandler.ts @@ -16,7 +16,7 @@ import { useSocketStore } from "../../../../../store/builder/store"; // import { upsertAisleApi } from "../../../../../services/factoryBuilder/aisle/upsertAisleApi"; // import { deleteAisleApi } from "../../../../../services/factoryBuilder/aisle/deleteAisleApi"; -function useRedoHandler() { +function use2DRedoHandler() { const { undoRedo2DStore, wallStore, floorStore, zoneStore, aisleStore } = useSceneContext(); const { redo2D, peekRedo2D } = undoRedo2DStore(); const { addWall, removeWall, updateWall } = wallStore(); @@ -352,4 +352,4 @@ function useRedoHandler() { return { handleRedo }; } -export default useRedoHandler; +export default use2DRedoHandler; diff --git a/app/src/modules/scene/controls/undoRedoControls/handlers/useUndoHandler.ts b/app/src/modules/scene/controls/undoRedoControls/handlers/use2DUndoHandler.ts similarity index 83% rename from app/src/modules/scene/controls/undoRedoControls/handlers/useUndoHandler.ts rename to app/src/modules/scene/controls/undoRedoControls/handlers/use2DUndoHandler.ts index c822ef9..9f7ff60 100644 --- a/app/src/modules/scene/controls/undoRedoControls/handlers/useUndoHandler.ts +++ b/app/src/modules/scene/controls/undoRedoControls/handlers/use2DUndoHandler.ts @@ -16,7 +16,7 @@ import { useSocketStore } from "../../../../../store/builder/store"; // import { upsertAisleApi } from "../../../../../services/factoryBuilder/aisle/upsertAisleApi"; // import { deleteAisleApi } from "../../../../../services/factoryBuilder/aisle/deleteAisleApi"; -function useUndoHandler() { +function use2DUndoHandler() { const { undoRedo2DStore, wallStore, floorStore, zoneStore, aisleStore } = useSceneContext(); const { undo2D, peekUndo2D } = undoRedo2DStore(); const { addWall, removeWall, updateWall } = wallStore(); @@ -67,38 +67,37 @@ function useUndoHandler() { } 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; + case 'Wall': createWallToBackend(point.lineData); break; + case 'Floor': createFloorToBackend(point.lineData); break; + case 'Zone': createZoneToBackend(point.lineData); break; + case 'Aisle': createAisleToBackend(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; + case 'Wall': removeWallToBackend(point.lineData.wallUuid); break; + case 'Floor': removeFloorToBackend(point.lineData.floorUuid); break; + case 'Zone': removeZoneToBackend(point.lineData.zoneUuid); break; + case 'Aisle': removeAisleToBackend(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; + case 'Wall': updateWallToBackend(point.lineData.wallUuid, point.lineData); break; + case 'Floor': updateFloorToBackend(point.lineData.floorUuid, point.lineData); break; + case 'Zone': updateZoneToBackend(point.lineData.zoneUuid, point.lineData); break; + case 'Aisle': updateAisleToBackend(point.lineData.aisleUuid, point.lineData); break; } }; - const createWallFromBackend = (wallData: Wall) => { + const createWallToBackend = (wallData: Wall) => { addWall(wallData); if (projectId) { // API @@ -119,7 +118,7 @@ function useUndoHandler() { } }; - const removeWallFromBackend = (wallUuid: string) => { + const removeWallToBackend = (wallUuid: string) => { removeWall(wallUuid); if (projectId) { // API @@ -140,7 +139,7 @@ function useUndoHandler() { } }; - const updateWallFromBackend = (wallUuid: string, updatedData: Wall) => { + const updateWallToBackend = (wallUuid: string, updatedData: Wall) => { updateWall(wallUuid, updatedData); if (projectId) { // API @@ -161,7 +160,7 @@ function useUndoHandler() { } }; - const createFloorFromBackend = (floorData: Floor) => { + const createFloorToBackend = (floorData: Floor) => { addFloor(floorData); if (projectId) { // API @@ -182,7 +181,7 @@ function useUndoHandler() { } }; - const removeFloorFromBackend = (floorUuid: string) => { + const removeFloorToBackend = (floorUuid: string) => { removeFloor(floorUuid); if (projectId) { // API @@ -203,7 +202,7 @@ function useUndoHandler() { } }; - const updateFloorFromBackend = (floorUuid: string, updatedData: Floor) => { + const updateFloorToBackend = (floorUuid: string, updatedData: Floor) => { updateFloor(floorUuid, updatedData); if (projectId) { // API @@ -224,7 +223,7 @@ function useUndoHandler() { } }; - const createZoneFromBackend = (zoneData: Zone) => { + const createZoneToBackend = (zoneData: Zone) => { addZone(zoneData); if (projectId) { // API @@ -245,7 +244,7 @@ function useUndoHandler() { } }; - const removeZoneFromBackend = (zoneUuid: string) => { + const removeZoneToBackend = (zoneUuid: string) => { removeZone(zoneUuid); if (projectId) { // API @@ -266,7 +265,7 @@ function useUndoHandler() { } }; - const updateZoneFromBackend = (zoneUuid: string, updatedData: Zone) => { + const updateZoneToBackend = (zoneUuid: string, updatedData: Zone) => { updateZone(zoneUuid, updatedData); if (projectId) { // API @@ -287,7 +286,7 @@ function useUndoHandler() { } }; - const createAisleFromBackend = (aisleData: Aisle) => { + const createAisleToBackend = (aisleData: Aisle) => { addAisle(aisleData); if (projectId) { // API @@ -308,7 +307,7 @@ function useUndoHandler() { } }; - const removeAisleFromBackend = (aisleUuid: string) => { + const removeAisleToBackend = (aisleUuid: string) => { removeAisle(aisleUuid); if (projectId) { // API @@ -329,7 +328,7 @@ function useUndoHandler() { } }; - const updateAisleFromBackend = (aisleUuid: string, updatedData: Aisle) => { + const updateAisleToBackend = (aisleUuid: string, updatedData: Aisle) => { updateAisle(aisleUuid, updatedData); if (projectId) { // API @@ -353,4 +352,4 @@ function useUndoHandler() { return { handleUndo }; } -export default useUndoHandler; \ No newline at end of file +export default use2DUndoHandler; \ No newline at end of file diff --git a/app/src/modules/scene/controls/undoRedoControls/handlers/use3DRedoHandler.ts b/app/src/modules/scene/controls/undoRedoControls/handlers/use3DRedoHandler.ts new file mode 100644 index 0000000..b44f6a7 --- /dev/null +++ b/app/src/modules/scene/controls/undoRedoControls/handlers/use3DRedoHandler.ts @@ -0,0 +1,318 @@ +import { useParams } from "react-router-dom"; +import { getUserData } from "../../../../../functions/getUserData"; +import { useVersionContext } from "../../../../builder/version/versionContext"; +import { useSceneContext } from "../../../sceneContext"; +import { useProductContext } from "../../../../simulation/products/productContext"; +import { useSocketStore } from "../../../../../store/builder/store"; + +import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi"; + +// import { setAssetsApi } from "../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi"; +// import { deleteFloorItem } from "../../../../../services/factoryBuilder/asset/floorAsset/deleteFloorItemApi"; + +function use3DRedoHandler() { + const { undoRedo3DStore, assetStore, productStore, eventStore } = useSceneContext(); + const { deleteEvent } = productStore(); + const { addEvent, removeEvent } = eventStore(); + const { updateAsset, removeAsset, addAsset } = assetStore(); + const { redo3D, peekRedo3D } = undoRedo3DStore(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); + const { userId, organization } = getUserData(); + const { projectId } = useParams(); + const { socket } = useSocketStore(); + + const updateBackend = ( + productName: string, + productUuid: string, + projectId: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productUuid: productUuid, + projectId: projectId, + eventDatas: eventData, + versionId: selectedVersion?.versionId || '', + }); + }; + + const handleRedo = () => { + const redoData = peekRedo3D(); + if (!redoData) return; + + if (redoData.type === 'Scene') { + const { actions } = redoData; + + actions.forEach(action => { + const { actionType } = action; + + if ('asset' in action) { + const asset = action.asset; + + if (actionType === 'Asset-Add') { + handleAdd(asset); + } else if (actionType === 'Asset-Delete') { + handleDelete(asset); + } else if (actionType === 'Asset-Update') { + handleUpdate(asset); + } else if (actionType === 'Asset-Copied') { + handleCopy(asset); + } else if (actionType === 'Asset-Duplicated') { + handleDuplicate(asset); + } + + } else if ('assets' in action) { + const assets = action.assets; + + if (actionType === 'Assets-Add') { + assets.forEach(handleAdd); + } else if (actionType === 'Assets-Delete') { + assets.forEach(handleDelete); + } else if (actionType === 'Assets-Update') { + assets.forEach(handleUpdate); + } else if (actionType === 'Assets-Copied') { + assets.forEach(handleCopy); + } else if (actionType === 'Assets-Duplicated') { + assets.forEach(handleDuplicate); + } + } + }); + } else if (redoData.type === 'UI') { + // Handle UI actions if needed + } + + redo3D(); + }; + + + const handleAdd = (asset: AssetData) => { + switch (asset.type) { + case 'Asset': addAssetToBackend(asset.assetData); break; + case 'WallAsset': addWallAssetToBackend(asset.assetData); break; + } + }; + + const handleDelete = (asset: AssetData) => { + switch (asset.type) { + case 'Asset': deleteAssetToBackend(asset.assetData); break; + case 'WallAsset': deleteWallAssetToBackend(asset.assetData); break; + } + } + + const handleUpdate = (asset: AssetData) => { + if (!asset.newData) return; + switch (asset.type) { + case 'Asset': updateAssetToBackend(asset.newData.modelUuid, asset.newData); break; + case 'WallAsset': updateWallAssetToBackend(asset.newData.modelUuid, asset.newData); break; + } + } + + const handleCopy = (asset: AssetData) => { + switch (asset.type) { + case 'Asset': copyAssetToBackend(asset.assetData); break; + case 'WallAsset': copyWallAssetToBackend(asset.assetData); break; + } + } + + const handleDuplicate = (asset: AssetData) => { + switch (asset.type) { + case 'Asset': duplicateAssetToBackend(asset.assetData); break; + case 'WallAsset': duplicateWallAssetToBackend(asset.assetData); break; + } + } + + + const addAssetToBackend = (assetData: Asset) => { + addAsset(assetData); + if (projectId) { + const data = { + organization, + modelUuid: assetData.modelUuid, + modelName: assetData.modelName, + assetId: assetData.assetId, + position: assetData.position, + rotation: { x: assetData.rotation[0], y: assetData.rotation[1], z: assetData.rotation[2] }, + isLocked: false, + isVisible: true, + eventData: {}, + socketId: socket.id, + versionId: selectedVersion?.versionId || '', + projectId, + userId + }; + + if (assetData.eventData) { + data.eventData = assetData.eventData; + addEvent(assetData.eventData as EventsSchema); + } + + // API + + // setAssetsApi(data); + + //SOCKET + + socket.emit("v1:model-asset:add", data); + } + } + + const deleteAssetToBackend = (assetData: Asset) => { + //REST + + // const response = deleteFloorItem(organization, assetData.modelUuid, assetData.modelName); + + //SOCKET + + const data = { + organization, + modelUuid: assetData.modelUuid, + modelName: assetData.modelName, + socketId: socket.id, + userId, + versionId: selectedVersion?.versionId || '', + projectId + } + + const response = socket.emit('v1:model-asset:delete', data) + + removeEvent(assetData.modelUuid); + const updatedEvents = deleteEvent(assetData.modelUuid); + + updatedEvents.forEach((event) => { + updateBackend( + selectedProduct.productName, + selectedProduct.productUuid, + projectId || '', + event + ); + }) + + if (response) { + + removeAsset(assetData.modelUuid); + } + } + + const updateAssetToBackend = (modelUuid: string, updatedData: Asset) => { + updateAsset(modelUuid, updatedData); + if (projectId) { + const data = { + organization, + modelUuid: updatedData.modelUuid, + modelName: updatedData.modelName, + assetId: updatedData.assetId, + position: updatedData.position, + rotation: { x: updatedData.rotation[0], y: updatedData.rotation[1], z: updatedData.rotation[2] }, + isLocked: false, + isVisible: true, + socketId: socket.id, + versionId: selectedVersion?.versionId || '', + projectId, + userId + }; + + // API + + // setAssetsApi(data); + + //SOCKET + + socket.emit("v1:model-asset:add", data); + } + } + + const copyAssetToBackend = (assetData: Asset) => { + addAsset(assetData); + if (projectId) { + const data = { + organization, + modelUuid: assetData.modelUuid, + modelName: assetData.modelName, + assetId: assetData.assetId, + position: assetData.position, + rotation: { x: assetData.rotation[0], y: assetData.rotation[1], z: assetData.rotation[2] }, + isLocked: false, + isVisible: true, + eventData: {}, + socketId: socket.id, + versionId: selectedVersion?.versionId || '', + projectId, + userId + }; + + if (assetData.eventData) { + data.eventData = assetData.eventData; + addEvent(assetData.eventData as EventsSchema); + } + + // API + + // setAssetsApi(data); + + //SOCKET + + socket.emit("v1:model-asset:add", data); + } + } + + const duplicateAssetToBackend = (assetData: Asset) => { + addAsset(assetData); + if (projectId) { + const data = { + organization, + modelUuid: assetData.modelUuid, + modelName: assetData.modelName, + assetId: assetData.assetId, + position: assetData.position, + rotation: { x: assetData.rotation[0], y: assetData.rotation[1], z: assetData.rotation[2] }, + isLocked: false, + isVisible: true, + eventData: {}, + socketId: socket.id, + versionId: selectedVersion?.versionId || '', + projectId, + userId + }; + + if (assetData.eventData) { + data.eventData = assetData.eventData; + addEvent(assetData.eventData as EventsSchema); + } + + // API + + // setAssetsApi(data); + + //SOCKET + + socket.emit("v1:model-asset:add", data); + } + } + + const addWallAssetToBackend = (assetData: WallAsset) => { + + } + + const deleteWallAssetToBackend = (assetData: WallAsset) => { + + } + + const updateWallAssetToBackend = (modelUuid: string, updatedData: WallAsset) => { + + } + + const copyWallAssetToBackend = (assetData: WallAsset) => { + + } + + const duplicateWallAssetToBackend = (assetData: WallAsset) => { + + } + + return { handleRedo }; +} + +export default use3DRedoHandler; diff --git a/app/src/modules/scene/controls/undoRedoControls/handlers/use3DUndoHandler.ts b/app/src/modules/scene/controls/undoRedoControls/handlers/use3DUndoHandler.ts new file mode 100644 index 0000000..50fd6cf --- /dev/null +++ b/app/src/modules/scene/controls/undoRedoControls/handlers/use3DUndoHandler.ts @@ -0,0 +1,323 @@ +import { useParams } from "react-router-dom"; +import { getUserData } from "../../../../../functions/getUserData"; +import { useVersionContext } from "../../../../builder/version/versionContext"; +import { useSceneContext } from "../../../sceneContext"; +import { useProductContext } from "../../../../simulation/products/productContext"; +import { useSocketStore } from "../../../../../store/builder/store"; + +import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi"; + +// import { setAssetsApi } from "../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi"; +// import { deleteFloorItem } from "../../../../../services/factoryBuilder/asset/floorAsset/deleteFloorItemApi"; + +function use3DUndoHandler() { + const { undoRedo3DStore, assetStore, productStore, eventStore } = useSceneContext(); + const { deleteEvent } = productStore(); + const { addEvent, removeEvent } = eventStore(); + const { updateAsset, removeAsset, addAsset } = assetStore(); + const { undo3D, peekUndo3D } = undoRedo3DStore(); + const { selectedVersionStore } = useVersionContext(); + const { selectedVersion } = selectedVersionStore(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); + const { userId, organization } = getUserData(); + const { projectId } = useParams(); + const { socket } = useSocketStore(); + + const updateBackend = ( + productName: string, + productUuid: string, + projectId: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productUuid: productUuid, + projectId: projectId, + eventDatas: eventData, + versionId: selectedVersion?.versionId || '', + }); + }; + + const handleUndo = () => { + const unDoData = peekUndo3D(); + if (!unDoData) return; + + if (unDoData.type === 'Scene') { + const { actions } = unDoData; + + actions.forEach(action => { + const { actionType } = action; + + if ('asset' in action) { + const asset = action.asset; + + if (actionType === 'Asset-Add') { + handleDelete(asset); + } else if (actionType === 'Asset-Delete') { + handleAdd(asset); + } else if (actionType === 'Asset-Update') { + handleUpdate(asset); + } else if (actionType === 'Asset-Copied') { + handleCopy(asset); + } else if (actionType === 'Asset-Duplicated') { + handleDuplicate(asset); + } + + } else if ('assets' in action) { + const assets = action.assets; + + if (actionType === 'Assets-Add') { + assets.forEach(handleDelete); + } else if (actionType === 'Assets-Delete') { + assets.forEach(handleAdd); + } else if (actionType === 'Assets-Update') { + assets.forEach(handleUpdate); + } else if (actionType === 'Assets-Copied') { + assets.forEach(handleCopy); + } else if (actionType === 'Assets-Duplicated') { + assets.forEach(handleDuplicate); + } + } + }); + } else if (unDoData.type === 'UI') { + // Handle UI actions if needed + } + + undo3D(); + }; + + + const handleAdd = (asset: AssetData) => { + switch (asset.type) { + case 'Asset': addAssetToBackend(asset.assetData); break; + case 'WallAsset': addWallAssetToBackend(asset.assetData); break; + } + }; + + const handleDelete = (asset: AssetData) => { + switch (asset.type) { + case 'Asset': deleteAssetToBackend(asset.assetData); break; + case 'WallAsset': deleteWallAssetToBackend(asset.assetData); break; + } + } + + const handleUpdate = (asset: AssetData) => { + switch (asset.type) { + case 'Asset': updateAssetToBackend(asset.assetData.modelUuid, asset.assetData); break; + case 'WallAsset': updateWallAssetToBackend(asset.assetData.modelUuid, asset.assetData); break; + } + } + + const handleCopy = (asset: AssetData) => { + switch (asset.type) { + case 'Asset': copyAssetToBackend(asset.assetData); break; + case 'WallAsset': copyWallAssetToBackend(asset.assetData); break; + } + } + + const handleDuplicate = (asset: AssetData) => { + switch (asset.type) { + case 'Asset': duplicateAssetToBackend(asset.assetData); break; + case 'WallAsset': duplicateWallAssetToBackend(asset.assetData); break; + } + } + + + const addAssetToBackend = (assetData: Asset) => { + addAsset(assetData); + if (projectId) { + const data = { + organization, + modelUuid: assetData.modelUuid, + modelName: assetData.modelName, + assetId: assetData.assetId, + position: assetData.position, + rotation: { x: assetData.rotation[0], y: assetData.rotation[1], z: assetData.rotation[2] }, + isLocked: false, + isVisible: true, + eventData: {}, + socketId: socket.id, + versionId: selectedVersion?.versionId || '', + projectId, + userId + }; + + if (assetData.eventData) { + data.eventData = assetData.eventData; + addEvent(assetData.eventData as EventsSchema); + } + + // API + + // setAssetsApi(data); + + //SOCKET + + socket.emit("v1:model-asset:add", data); + } + } + + const deleteAssetToBackend = (assetData: Asset) => { + //REST + + // const response = deleteFloorItem(organization, assetData.modelUuid, assetData.modelName); + + //SOCKET + + const data = { + organization, + modelUuid: assetData.modelUuid, + modelName: assetData.modelName, + socketId: socket.id, + userId, + versionId: selectedVersion?.versionId || '', + projectId + } + + const response = socket.emit('v1:model-asset:delete', data) + + removeEvent(assetData.modelUuid); + const updatedEvents = deleteEvent(assetData.modelUuid); + + updatedEvents.forEach((event) => { + updateBackend( + selectedProduct.productName, + selectedProduct.productUuid, + projectId || '', + event + ); + }) + + if (response) { + + removeAsset(assetData.modelUuid); + } + } + + const updateAssetToBackend = (modelUuid: string, updatedData: Asset) => { + updateAsset(modelUuid, updatedData); + if (projectId) { + const data = { + organization, + modelUuid: updatedData.modelUuid, + modelName: updatedData.modelName, + assetId: updatedData.assetId, + position: updatedData.position, + rotation: { x: updatedData.rotation[0], y: updatedData.rotation[1], z: updatedData.rotation[2] }, + isLocked: false, + isVisible: true, + socketId: socket.id, + versionId: selectedVersion?.versionId || '', + projectId, + userId + }; + + // API + + // setAssetsApi(data); + + //SOCKET + + socket.emit("v1:model-asset:add", data); + } + } + + const copyAssetToBackend = (assetData: Asset) => { + //REST + + // const response = deleteFloorItem(organization, assetData.modelUuid, assetData.modelName); + + //SOCKET + + const data = { + organization, + modelUuid: assetData.modelUuid, + modelName: assetData.modelName, + socketId: socket.id, + userId, + versionId: selectedVersion?.versionId || '', + projectId + } + + const response = socket.emit('v1:model-asset:delete', data) + + removeEvent(assetData.modelUuid); + const updatedEvents = deleteEvent(assetData.modelUuid); + + updatedEvents.forEach((event) => { + updateBackend( + selectedProduct.productName, + selectedProduct.productUuid, + projectId || '', + event + ); + }) + + if (response) { + + removeAsset(assetData.modelUuid); + } + } + + const duplicateAssetToBackend = (assetData: Asset) => { + //REST + + // const response = deleteFloorItem(organization, assetData.modelUuid, assetData.modelName); + + //SOCKET + + const data = { + organization, + modelUuid: assetData.modelUuid, + modelName: assetData.modelName, + socketId: socket.id, + userId, + versionId: selectedVersion?.versionId || '', + projectId + } + + const response = socket.emit('v1:model-asset:delete', data) + + removeEvent(assetData.modelUuid); + const updatedEvents = deleteEvent(assetData.modelUuid); + + updatedEvents.forEach((event) => { + updateBackend( + selectedProduct.productName, + selectedProduct.productUuid, + projectId || '', + event + ); + }) + + if (response) { + + removeAsset(assetData.modelUuid); + } + } + + const addWallAssetToBackend = (assetData: WallAsset) => { + + } + + const deleteWallAssetToBackend = (assetData: WallAsset) => { + + } + + const updateWallAssetToBackend = (modelUuid: string, updatedData: WallAsset) => { + + } + + const copyWallAssetToBackend = (assetData: WallAsset) => { + + } + + const duplicateWallAssetToBackend = (assetData: WallAsset) => { + + } + + return { handleUndo }; +} + +export default use3DUndoHandler; \ 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 index d45518c..1965087 100644 --- a/app/src/modules/scene/controls/undoRedoControls/undoRedo2D/undoRedo2DControls.tsx +++ b/app/src/modules/scene/controls/undoRedoControls/undoRedo2D/undoRedo2DControls.tsx @@ -4,21 +4,21 @@ import { detectModifierKeys } from '../../../../../utils/shortcutkeys/detectModi import { useSocketStore, useToggleView } from '../../../../../store/builder/store'; import { useVersionContext } from '../../../../builder/version/versionContext'; -import useUndoHandler from '../handlers/useUndoHandler'; -import useRedoHandler from '../handlers/useRedoHandler'; +import use2DUndoHandler from '../handlers/use2DUndoHandler'; +import use2DRedoHandler from '../handlers/use2DRedoHandler'; function UndoRedo2DControls() { const { undoRedo2DStore } = useSceneContext(); const { undoStack, redoStack } = undoRedo2DStore(); const { toggleView } = useToggleView(); - const { handleUndo } = useUndoHandler(); - const { handleRedo } = useRedoHandler(); + const { handleUndo } = use2DUndoHandler(); + const { handleRedo } = use2DRedoHandler(); const { socket } = useSocketStore(); const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); useEffect(() => { - console.log(undoStack, redoStack); + // console.log(undoStack, redoStack); }, [undoStack, redoStack]); useEffect(() => { diff --git a/app/src/modules/scene/controls/undoRedoControls/undoRedo3D/undoRedo3DControls.tsx b/app/src/modules/scene/controls/undoRedoControls/undoRedo3D/undoRedo3DControls.tsx new file mode 100644 index 0000000..9331320 --- /dev/null +++ b/app/src/modules/scene/controls/undoRedoControls/undoRedo3D/undoRedo3DControls.tsx @@ -0,0 +1,51 @@ +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 useModuleStore from '../../../../../store/useModuleStore'; + +import use3DUndoHandler from '../handlers/use3DUndoHandler'; +import use3DRedoHandler from '../handlers/use3DRedoHandler'; + +function UndoRedo3DControls() { + const { undoRedo3DStore } = useSceneContext(); + const { undoStack, redoStack } = undoRedo3DStore(); + const { toggleView } = useToggleView(); + const { activeModule } = useModuleStore(); + const { handleUndo } = use3DUndoHandler(); + const { handleRedo } = use3DRedoHandler(); + 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, activeModule]); + + return null; +} + +export default UndoRedo3DControls; \ No newline at end of file diff --git a/app/src/modules/scene/sceneContext.tsx b/app/src/modules/scene/sceneContext.tsx index 6dbfce8..73e5f9c 100644 --- a/app/src/modules/scene/sceneContext.tsx +++ b/app/src/modules/scene/sceneContext.tsx @@ -8,6 +8,7 @@ import { createZoneStore, ZoneStoreType } from '../../store/builder/useZoneStore import { createFloorStore, FloorStoreType } from '../../store/builder/useFloorStore'; import { createUndoRedo2DStore, UndoRedo2DStoreType } from '../../store/builder/useUndoRedo2DStore'; +import { createUndoRedo3DStore, UndoRedo3DStoreType } from '../../store/builder/useUndoRedo3DStore'; import { createEventStore, EventStoreType } from '../../store/simulation/useEventsStore'; import { createProductStore, ProductStoreType } from '../../store/simulation/useProductStore'; @@ -31,6 +32,7 @@ type SceneContextValue = { floorStore: FloorStoreType, undoRedo2DStore: UndoRedo2DStoreType, + undoRedo3DStore: UndoRedo3DStoreType, eventStore: EventStoreType, productStore: ProductStoreType, @@ -70,6 +72,7 @@ export function SceneProvider({ const floorStore = useMemo(() => createFloorStore(), []); const undoRedo2DStore = useMemo(() => createUndoRedo2DStore(), []); + const undoRedo3DStore = useMemo(() => createUndoRedo3DStore(), []); const eventStore = useMemo(() => createEventStore(), []); const productStore = useMemo(() => createProductStore(), []); @@ -94,6 +97,7 @@ export function SceneProvider({ zoneStore.getState().clearZones(); floorStore.getState().clearFloors(); undoRedo2DStore.getState().clearUndoRedo2D(); + undoRedo3DStore.getState().clearUndoRedo3D(); eventStore.getState().clearEvents(); productStore.getState().clearProducts(); materialStore.getState().clearMaterials(); @@ -106,7 +110,7 @@ export function SceneProvider({ craneStore.getState().clearCranes(); humanEventManagerRef.current.humanStates = []; craneEventManagerRef.current.craneStates = []; - }, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, undoRedo2DStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, craneStore]); + }, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, undoRedo2DStore, undoRedo3DStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, craneStore]); const contextValue = useMemo(() => ( { @@ -117,6 +121,7 @@ export function SceneProvider({ zoneStore, floorStore, undoRedo2DStore, + undoRedo3DStore, eventStore, productStore, materialStore, @@ -132,7 +137,7 @@ export function SceneProvider({ clearStores, layout } - ), [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, undoRedo2DStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, craneStore, clearStores, layout]); + ), [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, undoRedo2DStore, undoRedo3DStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, craneStore, clearStores, layout]); return ( diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx index ab6041c..b43c7fa 100644 --- a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx @@ -22,7 +22,7 @@ function IKInstance({ setIkSolver, armBot }: IKInstanceProps) { const trySetup = () => { const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid); - if (!targetMesh || !targetMesh.userData.iks || targetMesh.userData.iks.length < 1) { + if (!targetMesh || !targetMesh.userData.fieldData || targetMesh.userData.fieldData.length < 1) { retryId = setTimeout(trySetup, 100); return; } @@ -34,8 +34,8 @@ function IKInstance({ setIkSolver, armBot }: IKInstanceProps) { }); if (!OOI.Target_Bone || !OOI.Skinned_Mesh) return; - const rawIks: IK[] = targetMesh.userData.iks; - const iks = rawIks.map((ik) => ({ + const rawIks: IK[] = targetMesh.userData.fieldData; + const fieldData = rawIks.map((ik) => ({ target: ik.target, effector: ik.effector, links: ik.links.map((link) => ({ @@ -51,10 +51,10 @@ function IKInstance({ setIkSolver, armBot }: IKInstanceProps) { minheight: ik.minheight, })); - const solver = new CCDIKSolver(OOI.Skinned_Mesh, iks); + const solver = new CCDIKSolver(OOI.Skinned_Mesh, fieldData); setIkSolver(solver); - // const helper = new CCDIKHelper(OOI.Skinned_Mesh, iks, 0.05) + // const helper = new CCDIKHelper(OOI.Skinned_Mesh, fieldData, 0.05) // scene.add(helper); }; diff --git a/app/src/modules/simulation/spatialUI/arm/armBotUI.tsx b/app/src/modules/simulation/spatialUI/arm/armBotUI.tsx index dfd500a..b39076e 100644 --- a/app/src/modules/simulation/spatialUI/arm/armBotUI.tsx +++ b/app/src/modules/simulation/spatialUI/arm/armBotUI.tsx @@ -173,8 +173,8 @@ const ArmBotUI = () => { const targetMesh = scene?.getObjectByProperty("uuid", selectedArmBotData?.modelUuid || ''); - const iks = targetMesh?.userData?.iks; - const firstIK = Array.isArray(iks) && iks.length > 0 ? iks[0] : {}; + const fieldData = targetMesh?.userData?.fieldData; + const firstIK = Array.isArray(fieldData) && fieldData.length > 0 ? fieldData[0] : {}; const { handlePointerDown } = useDraggableGLTF( updatePointToState, diff --git a/app/src/services/simulation/ik/getAssetIKs.ts b/app/src/services/factoryBuilder/asset/floorAsset/getAssetField.ts similarity index 80% rename from app/src/services/simulation/ik/getAssetIKs.ts rename to app/src/services/factoryBuilder/asset/floorAsset/getAssetField.ts index cfc6ad5..83567a9 100644 --- a/app/src/services/simulation/ik/getAssetIKs.ts +++ b/app/src/services/factoryBuilder/asset/floorAsset/getAssetField.ts @@ -1,9 +1,9 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; -export const getAssetIksApi = async (assetId: string) => { +export const getAssetFieldApi = async (assetId: string) => { try { const response = await fetch( - `${url_Backend_dwinzo}/api/v2/getAssetIks/${assetId}`, + `${url_Backend_dwinzo}/api/v2/getAssetField/${assetId}`, { method: "GET", headers: { @@ -20,13 +20,13 @@ export const getAssetIksApi = async (assetId: string) => { } if (!response.ok) { - console.error("Failed to fetch assetIks"); + console.error("Failed to fetch asset field"); } const result = await response.json(); return result; } catch (error) { - echo.error("Failed to get assetIks"); + echo.error("Failed to get asset field"); if (error instanceof Error) { console.log(error.message); } else { diff --git a/app/src/store/builder/useUndoRedo3DStore.ts b/app/src/store/builder/useUndoRedo3DStore.ts new file mode 100644 index 0000000..e8c3ce3 --- /dev/null +++ b/app/src/store/builder/useUndoRedo3DStore.ts @@ -0,0 +1,78 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; +import { undoRedoConfig } from '../../types/world/worldConstants'; + +type UndoRedo3DStore = { + undoStack: UndoRedo3DTypes[]; + redoStack: UndoRedo3DTypes[]; + + push3D: (entry: UndoRedo3DTypes) => void; + undo3D: () => UndoRedo3DTypes | undefined; + redo3D: () => UndoRedo3DTypes | undefined; + clearUndoRedo3D: () => void; + + peekUndo3D: () => UndoRedo3DTypes | undefined; + peekRedo3D: () => UndoRedo3DTypes | undefined; +}; + +export const createUndoRedo3DStore = () => { + return create()( + immer((set, get) => ({ + undoStack: [], + redoStack: [], + + push3D: (entry) => { + set((state) => { + state.undoStack.push(entry); + + if (state.undoStack.length > undoRedoConfig.undoRedoCount) { + state.undoStack.shift(); + } + + state.redoStack = []; + }); + }, + + undo3D: () => { + let lastAction: UndoRedo3DTypes | undefined; + set((state) => { + lastAction = state.undoStack.pop(); + if (lastAction) { + state.redoStack.unshift(lastAction); + } + }); + return lastAction; + }, + + redo3D: () => { + let redoAction: UndoRedo3DTypes | undefined; + set((state) => { + redoAction = state.redoStack.shift(); + if (redoAction) { + state.undoStack.push(redoAction); + } + }); + return redoAction; + }, + + clearUndoRedo3D: () => { + set((state) => { + state.undoStack = []; + state.redoStack = []; + }); + }, + + peekUndo3D: () => { + const stack = get().undoStack; + return stack.length > 0 ? stack[stack.length - 1] : undefined; + }, + + peekRedo3D: () => { + const stack = get().redoStack; + return stack.length > 0 ? stack[0] : undefined; + }, + })) + ); +} + +export type UndoRedo3DStoreType = ReturnType; \ No newline at end of file diff --git a/app/src/types/builderTypes.d.ts b/app/src/types/builderTypes.d.ts index 63ed97b..3487fc6 100644 --- a/app/src/types/builderTypes.d.ts +++ b/app/src/types/builderTypes.d.ts @@ -253,4 +253,69 @@ type UndoRedo2DTypes = UndoRedo2DDraw | UndoRedo2DUi type UndoRedo2D = { undoStack: UndoRedo2DTypes[]; redoStack: UndoRedo2DTypes[]; +}; + + +// Undo/Redo 3D + +type AssetType = { + type: "Asset"; + assetData: Asset; + newData?: Asset; + eventMetaData?: EventsSchema; + timeStap: string; +} + +type WallAssetType = { + type: "WallAsset"; + assetData: WallAsset; + newData?: WallAsset; + timeStap: string; +} + +type AssetData = AssetType | WallAssetType; + +type UndoRedo3DActionBuilderSchema = { + module: "builder"; + actionType: "Asset-Add" | "Asset-Delete" | "Asset-Update" | "Asset-Duplicated" | "Asset-Copied" | "Wall-Asset-Add" | "Wall-Asset-Delete" | "Wall-Asset-Update"; + asset: AssetData; +} + +type UndoRedo3DActionSimulationSchema = { + module: "simulation"; + actionType: ''; +} + +type UndoRedo3DActionSchema = UndoRedo3DActionBuilderSchema | UndoRedo3DActionSimulationSchema; + +type UndoRedo3DActionsBuilderSchema = { + module: "builder"; + actionType: "Assets-Add" | "Assets-Delete" | "Assets-Update" | "Assets-Duplicated" | "Assets-Copied" | "Wall-Assets-Add" | "Wall-Assets-Delete" | "Wall-Assets-Update"; + assets: AssetData[]; +} + +type UndoRedo3DActionsSimulationSchema = { + module: "simulation"; + actionType: ''; +} + +type UndoRedo3DActionsSchema = UndoRedo3DActionsBuilderSchema | UndoRedo3DActionsSimulationSchema; + +type UndoRedo3DAction = UndoRedo3DActionSchema | UndoRedo3DActionsSchema; + +type UndoRedo3DDraw = { + type: 'Scene'; + actions: UndoRedo3DAction[]; +}; + +type UndoRedo3DUi = { + type: 'UI'; + action: any; // Define UI actions as needed +} + +type UndoRedo3DTypes = UndoRedo3DDraw | UndoRedo3DUi; + +type UndoRedo3D = { + undoStack: UndoRedo3DTypes[]; + redoStack: UndoRedo3DTypes[]; }; \ No newline at end of file diff --git a/app/src/utils/shortcutkeys/handleShortcutKeys.ts b/app/src/utils/shortcutkeys/handleShortcutKeys.ts index ffd9ddf..7abbe2f 100644 --- a/app/src/utils/shortcutkeys/handleShortcutKeys.ts +++ b/app/src/utils/shortcutkeys/handleShortcutKeys.ts @@ -189,12 +189,9 @@ const KeyPressListener: React.FC = () => { }; const handleKeyPress = (event: KeyboardEvent) => { - if (isTextInput(document.activeElement)) return; - const keyCombination = detectModifierKeys(event); - if (isTextInput(document.activeElement) && keyCombination !== "ESCAPE") - return; + if (isTextInput(document.activeElement) && keyCombination !== "ESCAPE") return; if (keyCombination === "ESCAPE") { setWalkMode(false);