Undo/redo returns frame where was a change (as it was done in previous version)

main
Boris Sekachev 6 years ago
parent 5e2f2ecf31
commit c55cbdefe5

@ -427,7 +427,10 @@
for (const object of objectsForMerge) {
object.removed = true;
}
}, [...objectsForMerge.map((object) => object.clientID), trackModel.clientID]);
}, [
...objectsForMerge
.map((object) => object.clientID), trackModel.clientID,
], objectStates[0].frame);
}
split(objectState, frame) {
@ -522,7 +525,7 @@
object.removed = true;
prevTrack.removed = false;
nextTrack.removed = false;
}, [object.clientID, prevTrack.clientID, nextTrack.clientID]);
}, [object.clientID, prevTrack.clientID, nextTrack.clientID], frame);
}
group(objectStates, reset) {
@ -554,7 +557,7 @@
objectsForGroup.forEach((object, idx) => {
object.group = redoGroups[idx];
});
}, objectsForGroup.map((object) => object.clientID));
}, objectsForGroup.map((object) => object.clientID), objectStates[0].frame);
return groupIdx;
}
@ -790,7 +793,7 @@
importedArray.forEach((object) => {
object.removed = false;
});
}, importedArray.map((object) => object.clientID));
}, importedArray.map((object) => object.clientID), objectStates[0].frame);
}
select(objectStates, x, y) {

@ -12,17 +12,18 @@ class AnnotationHistory {
get() {
return {
undo: this._undo.map((undo) => undo.action),
redo: this._redo.map((redo) => redo.action),
undo: this._undo.map((undo) => [undo.action, undo.frame]),
redo: this._redo.map((redo) => [redo.action, redo.frame]),
};
}
do(action, undo, redo, clientIDs) {
do(action, undo, redo, clientIDs, frame) {
const actionItem = {
clientIDs,
action,
undo,
redo,
frame,
};
this._undo = this._undo.slice(-MAX_HISTORY_LENGTH + 1);

@ -178,7 +178,7 @@
injection.groups.max = Math.max(injection.groups.max, this.group);
}
_saveLock(lock) {
_saveLock(lock, frame) {
const undoLock = this.lock;
const redoLock = lock;
@ -186,12 +186,12 @@
this.lock = undoLock;
}, () => {
this.lock = redoLock;
}, [this.clientID]);
}, [this.clientID], frame);
this.lock = lock;
}
_saveColor(color) {
_saveColor(color, frame) {
const undoColor = this.color;
const redoColor = color;
@ -199,12 +199,12 @@
this.color = undoColor;
}, () => {
this.color = redoColor;
}, [this.clientID]);
}, [this.clientID], frame);
this.color = color;
}
_saveHidden(hidden) {
_saveHidden(hidden, frame) {
const undoHidden = this.hidden;
const redoHidden = hidden;
@ -212,12 +212,12 @@
this.hidden = undoHidden;
}, () => {
this.hidden = redoHidden;
}, [this.clientID]);
}, [this.clientID], frame);
this.hidden = hidden;
}
_saveLabel(label) {
_saveLabel(label, frame) {
const undoLabel = this.label;
const redoLabel = label;
const undoAttributes = { ...this.attributes };
@ -232,10 +232,10 @@
}, () => {
this.label = redoLabel;
this.attributes = redoAttributes;
}, [this.clientID]);
}, [this.clientID], frame);
}
_saveAttributes(attributes) {
_saveAttributes(attributes, frame) {
const undoAttributes = { ...this.attributes };
for (const attrID of Object.keys(attributes)) {
@ -248,7 +248,7 @@
this.attributes = undoAttributes;
}, () => {
this.attributes = redoAttributes;
}, [this.clientID]);
}, [this.clientID], frame);
}
_validateStateBeforeSave(frame, data, updated) {
@ -368,7 +368,7 @@
}
}
delete(force) {
delete(frame, force) {
if (!this.lock || force) {
this.removed = true;
@ -376,7 +376,7 @@
this.removed = false;
}, () => {
this.removed = true;
}, [this.clientID]);
}, [this.clientID], frame);
}
return this.removed;
@ -392,7 +392,7 @@
this.shapeType = null;
}
_savePinned(pinned) {
_savePinned(pinned, frame) {
const undoPinned = this.pinned;
const redoPinned = pinned;
@ -400,7 +400,7 @@
this.pinned = undoPinned;
}, () => {
this.pinned = redoPinned;
}, [this.clientID]);
}, [this.clientID], frame);
this.pinned = pinned;
}
@ -483,7 +483,7 @@
};
}
_savePoints(points) {
_savePoints(points, frame) {
const undoPoints = this.points;
const redoPoints = points;
@ -491,12 +491,12 @@
this.points = undoPoints;
}, () => {
this.points = redoPoints;
}, [this.clientID]);
}, [this.clientID], frame);
this.points = points;
}
_saveOccluded(occluded) {
_saveOccluded(occluded, frame) {
const undoOccluded = this.occluded;
const redoOccluded = occluded;
@ -504,12 +504,12 @@
this.occluded = undoOccluded;
}, () => {
this.occluded = redoOccluded;
}, [this.clientID]);
}, [this.clientID], frame);
this.occluded = occluded;
}
_saveZOrder(zOrder) {
_saveZOrder(zOrder, frame) {
const undoZOrder = this.zOrder;
const redoZOrder = zOrder;
@ -517,7 +517,7 @@
this.zOrder = undoZOrder;
}, () => {
this.zOrder = redoZOrder;
}, [this.clientID]);
}, [this.clientID], frame);
this.zOrder = zOrder;
}
@ -538,39 +538,39 @@
// Now when all fields are validated, we can apply them
if (updated.label) {
this._saveLabel(data.label);
this._saveLabel(data.label, frame);
}
if (updated.attributes) {
this._saveAttributes(data.attributes);
this._saveAttributes(data.attributes, frame);
}
if (updated.points && fittedPoints.length) {
this._savePoints(fittedPoints);
this._savePoints(fittedPoints, frame);
}
if (updated.occluded) {
this._saveOccluded(data.occluded);
this._saveOccluded(data.occluded, frame);
}
if (updated.zOrder) {
this._saveZOrder(data.zOrder);
this._saveZOrder(data.zOrder, frame);
}
if (updated.lock) {
this._saveLock(data.lock);
this._saveLock(data.lock, frame);
}
if (updated.pinned) {
this._savePinned(data.pinned);
this._savePinned(data.pinned, frame);
}
if (updated.color) {
this._saveColor(data.color);
this._saveColor(data.color, frame);
}
if (updated.hidden) {
this._saveHidden(data.hidden);
this._saveHidden(data.hidden, frame);
}
this.updateTimestamp(updated);
@ -745,7 +745,7 @@
return result;
}
_saveLabel(label) {
_saveLabel(label, frame) {
const undoLabel = this.label;
const redoLabel = label;
const undoAttributes = {
@ -783,10 +783,10 @@
for (const mutable of redoAttributes.mutable) {
this.shapes[mutable.frame].attributes = mutable.attributes;
}
}, [this.clientID]);
}, [this.clientID], frame);
}
_saveAttributes(frame, attributes) {
_saveAttributes(attributes, frame) {
const current = this.get(frame);
const labelAttributes = this.label.attributes
.reduce((accumulator, value) => {
@ -858,7 +858,7 @@
if (redoShape) {
this.shapes[frame] = redoShape;
}
}, [this.clientID]);
}, [this.clientID], frame);
}
_appendShapeActionToHistory(actionType, frame, undoShape, redoShape) {
@ -874,10 +874,10 @@
} else {
this.shapes[frame] = redoShape;
}
}, [this.clientID]);
}, [this.clientID], frame);
}
_savePoints(frame, points) {
_savePoints(points, frame) {
const current = this.get(frame);
const wasKeyframe = frame in this.shapes;
const undoShape = wasKeyframe ? this.shapes[frame] : undefined;
@ -921,7 +921,7 @@
);
}
_saveOccluded(frame, occluded) {
_saveOccluded(occluded, frame) {
const current = this.get(frame);
const wasKeyframe = frame in this.shapes;
const undoShape = wasKeyframe ? this.shapes[frame] : undefined;
@ -943,7 +943,7 @@
);
}
_saveZOrder(frame, zOrder) {
_saveZOrder(zOrder, frame) {
const current = this.get(frame);
const wasKeyframe = frame in this.shapes;
const undoShape = wasKeyframe ? this.shapes[frame] : undefined;
@ -1007,27 +1007,27 @@
const fittedPoints = this._validateStateBeforeSave(frame, data, updated);
if (updated.label) {
this._saveLabel(data.label);
this._saveLabel(data.label, frame);
}
if (updated.lock) {
this._saveLock(data.lock);
this._saveLock(data.lock, frame);
}
if (updated.pinned) {
this._savePinned(data.pinned);
this._savePinned(data.pinned, frame);
}
if (updated.color) {
this._saveColor(data.color);
this._saveColor(data.color, frame);
}
if (updated.hidden) {
this._saveHidden(data.hidden);
this._saveHidden(data.hidden, frame);
}
if (updated.points && fittedPoints.length) {
this._savePoints(frame, fittedPoints);
this._savePoints(fittedPoints, frame);
}
if (updated.outside) {
@ -1035,15 +1035,15 @@
}
if (updated.occluded) {
this._saveOccluded(frame, data.occluded);
this._saveOccluded(data.occluded, frame);
}
if (updated.zOrder) {
this._saveZOrder(frame, data.zOrder);
this._saveZOrder(data.zOrder, frame);
}
if (updated.attributes) {
this._saveAttributes(frame, data.attributes);
this._saveAttributes(data.attributes, frame);
}
if (updated.keyframe) {
@ -1161,19 +1161,19 @@
// Now when all fields are validated, we can apply them
if (updated.label) {
this._saveLabel(data.label);
this._saveLabel(data.label, frame);
}
if (updated.attributes) {
this._saveAttributes(data.attributes);
this._saveAttributes(data.attributes, frame);
}
if (updated.lock) {
this._saveLock(data.lock);
this._saveLock(data.lock, frame);
}
if (updated.color) {
this._saveColor(data.color);
this._saveColor(data.color, frame);
}
this.updateTimestamp(updated);

@ -398,14 +398,16 @@
* @memberof module:API.cvat.classes.ObjectState
* @readonly
* @instance
* @param {integer} frame current frame number
* @param {boolean} [force=false] delete object even if it is locked
* @async
* @returns {boolean} true if object has been deleted
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError}
*/
async delete(force = false) {
async delete(frame, force = false) {
const result = await PluginRegistry
.apiWrapper.call(this, ObjectState.prototype.delete, force);
.apiWrapper.call(this, ObjectState.prototype.delete, frame, force);
return result;
}
}
@ -420,9 +422,13 @@
};
// Delete element from a collection which contains it
ObjectState.prototype.delete.implementation = async function (force) {
ObjectState.prototype.delete.implementation = async function (frame, force) {
if (this.__internal && this.__internal.delete) {
return this.__internal.delete(force);
if (!Number.isInteger(+frame) || +frame < 0) {
throw ArgumentError('Frame argument must be a non negative integer');
}
return this.__internal.delete(frame, force);
}
return false;

@ -504,6 +504,7 @@
* @returns {HistoryActions}
* @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError}
* @returns {[string, number][]} array of pairs [action name, frame number]
* @instance
* @async
*/

@ -290,86 +290,6 @@ export function changeAnnotationsFilters(filters: string[]): AnyAction {
};
}
export function undoActionAsync(sessionInstance: any, frame: number):
ThunkAction<Promise<void>, {}, {}, AnyAction> {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
const state = getStore().getState();
const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters();
// TODO: use affected IDs as an optimization
const [undoName] = state.annotation.annotations.history.undo.slice(-1);
const undoLog = await sessionInstance.logger.log(LogType.undoAction, {
name: undoName,
count: 1,
}, true);
await sessionInstance.actions.undo();
const history = await sessionInstance.actions.get();
const states = await sessionInstance.annotations
.get(frame, showAllInterpolationTracks, filters);
const [minZ, maxZ] = computeZRange(states);
await undoLog.close();
dispatch({
type: AnnotationActionTypes.UNDO_ACTION_SUCCESS,
payload: {
history,
states,
minZ,
maxZ,
},
});
} catch (error) {
dispatch({
type: AnnotationActionTypes.UNDO_ACTION_FAILED,
payload: {
error,
},
});
}
};
}
export function redoActionAsync(sessionInstance: any, frame: number):
ThunkAction<Promise<void>, {}, {}, AnyAction> {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
const state = getStore().getState();
const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters();
// TODO: use affected IDs as an optimization
const [redoName] = state.annotation.annotations.history.redo.slice(-1);
const redoLog = await sessionInstance.logger.log(LogType.redoAction, {
name: redoName,
count: 1,
}, true);
await sessionInstance.actions.redo();
const history = await sessionInstance.actions.get();
const states = await sessionInstance.annotations
.get(frame, showAllInterpolationTracks, filters);
const [minZ, maxZ] = computeZRange(states);
await redoLog.close();
dispatch({
type: AnnotationActionTypes.REDO_ACTION_SUCCESS,
payload: {
history,
states,
minZ,
maxZ,
},
});
} catch (error) {
dispatch({
type: AnnotationActionTypes.REDO_ACTION_FAILED,
payload: {
error,
},
});
}
};
}
export function updateCanvasContextMenu(
visible: boolean,
left: number,
@ -625,7 +545,9 @@ ThunkAction<Promise<void>, {}, {}, AnyAction> {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
await sessionInstance.logger.log(LogType.deleteObject, { count: 1 });
const removed = await objectState.delete(force);
const { frame } = receiveAnnotationsParameters();
const removed = await objectState.delete(frame, force);
const history = await sessionInstance.actions.get();
if (removed) {
@ -817,6 +739,90 @@ ThunkAction<Promise<void>, {}, {}, AnyAction> {
};
}
export function undoActionAsync(sessionInstance: any, frame: number):
ThunkAction<Promise<void>, {}, {}, AnyAction> {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
const state = getStore().getState();
const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters();
// TODO: use affected IDs as an optimization
const [undo] = state.annotation.annotations.history.undo.slice(-1);
const undoLog = await sessionInstance.logger.log(LogType.undoAction, {
name: undo[0],
frame: undo[1],
count: 1,
}, true);
dispatch(changeFrameAsync(undo[1]));
await sessionInstance.actions.undo();
const history = await sessionInstance.actions.get();
const states = await sessionInstance.annotations
.get(frame, showAllInterpolationTracks, filters);
const [minZ, maxZ] = computeZRange(states);
await undoLog.close();
dispatch({
type: AnnotationActionTypes.UNDO_ACTION_SUCCESS,
payload: {
history,
states,
minZ,
maxZ,
},
});
} catch (error) {
dispatch({
type: AnnotationActionTypes.UNDO_ACTION_FAILED,
payload: {
error,
},
});
}
};
}
export function redoActionAsync(sessionInstance: any, frame: number):
ThunkAction<Promise<void>, {}, {}, AnyAction> {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
const state = getStore().getState();
const { filters, showAllInterpolationTracks } = receiveAnnotationsParameters();
// TODO: use affected IDs as an optimization
const [redo] = state.annotation.annotations.history.redo.slice(-1);
const redoLog = await sessionInstance.logger.log(LogType.redoAction, {
name: redo[0],
frame: redo[1],
count: 1,
}, true);
dispatch(changeFrameAsync(redo[1]));
await sessionInstance.actions.redo();
const history = await sessionInstance.actions.get();
const states = await sessionInstance.annotations
.get(frame, showAllInterpolationTracks, filters);
const [minZ, maxZ] = computeZRange(states);
await redoLog.close();
dispatch({
type: AnnotationActionTypes.REDO_ACTION_SUCCESS,
payload: {
history,
states,
minZ,
maxZ,
},
});
} catch (error) {
dispatch({
type: AnnotationActionTypes.REDO_ACTION_FAILED,
payload: {
error,
},
});
}
};
}
export function rotateCurrentFrame(rotation: Rotation): AnyAction {
const state: CombinedState = getStore().getState();

@ -104,8 +104,8 @@ function mapStateToProps(state: CombinedState): StateToProps {
savingStatuses,
frameNumber,
jobInstance,
undoAction: history.undo[history.undo.length - 1],
redoAction: history.redo[history.redo.length - 1],
undoAction: history.undo.length ? history.undo[history.undo.length - 1][0] : undefined,
redoAction: history.redo.length ? history.redo[history.redo.length - 1][0] : undefined,
autoSave,
autoSaveInterval,
workspace,

@ -349,8 +349,8 @@ export interface AnnotationState {
filtersHistory: string[];
resetGroupFlag: boolean;
history: {
undo: string[];
redo: string[];
undo: [string, number][];
redo: [string, number][];
};
saving: {
uploading: boolean;

Loading…
Cancel
Save