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

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

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

@ -398,14 +398,16 @@
* @memberof module:API.cvat.classes.ObjectState * @memberof module:API.cvat.classes.ObjectState
* @readonly * @readonly
* @instance * @instance
* @param {integer} frame current frame number
* @param {boolean} [force=false] delete object even if it is locked * @param {boolean} [force=false] delete object even if it is locked
* @async * @async
* @returns {boolean} true if object has been deleted * @returns {boolean} true if object has been deleted
* @throws {module:API.cvat.exceptions.PluginError} * @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 const result = await PluginRegistry
.apiWrapper.call(this, ObjectState.prototype.delete, force); .apiWrapper.call(this, ObjectState.prototype.delete, frame, force);
return result; return result;
} }
} }
@ -420,9 +422,13 @@
}; };
// Delete element from a collection which contains it // 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) { 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; return false;

@ -504,6 +504,7 @@
* @returns {HistoryActions} * @returns {HistoryActions}
* @throws {module:API.cvat.exceptions.PluginError} * @throws {module:API.cvat.exceptions.PluginError}
* @throws {module:API.cvat.exceptions.ArgumentError} * @throws {module:API.cvat.exceptions.ArgumentError}
* @returns {[string, number][]} array of pairs [action name, frame number]
* @instance * @instance
* @async * @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( export function updateCanvasContextMenu(
visible: boolean, visible: boolean,
left: number, left: number,
@ -625,7 +545,9 @@ ThunkAction<Promise<void>, {}, {}, AnyAction> {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => { return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try { try {
await sessionInstance.logger.log(LogType.deleteObject, { count: 1 }); 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(); const history = await sessionInstance.actions.get();
if (removed) { 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 { export function rotateCurrentFrame(rotation: Rotation): AnyAction {
const state: CombinedState = getStore().getState(); const state: CombinedState = getStore().getState();

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

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

Loading…
Cancel
Save