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

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