added undo redo for builder (not for simulation data)

This commit is contained in:
2025-08-11 16:59:36 +05:30
parent a7dc3665ca
commit 78e1ccf39f
22 changed files with 1179 additions and 72 deletions

View File

@@ -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!");
}

View File

@@ -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<THREE.Box3 | null>(null);
const [isSelected, setIsSelected] = useState(false);
const groupRef = useRef<THREE.Group>(null);
const [ikData, setIkData] = useState<any>();
const [fieldData, setFieldData] = useState<any>();
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) => {

View File

@@ -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<CameraControls>(null);
@@ -144,6 +145,8 @@ export default function Controls() {
<UndoRedo2DControls />
<UndoRedo3DControls />
<TransformControl />
</>

View File

@@ -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();
};

View File

@@ -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();
};

View File

@@ -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();

View File

@@ -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]);

View File

@@ -31,8 +31,9 @@ const SelectionControls3D: React.FC = () => {
const boundingBoxRef = useRef<THREE.Mesh>();
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) => {

View File

@@ -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") {

View File

@@ -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;

View File

@@ -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;
export default use2DUndoHandler;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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(() => {

View File

@@ -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;

View File

@@ -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 (
<SceneContext.Provider value={contextValue}>

View File

@@ -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);
};

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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<UndoRedo3DStore>()(
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<typeof createUndoRedo3DStore>;

View File

@@ -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[];
};

View File

@@ -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);