From 3199fbbdf9f340aa796aa15771c92d5099323f54 Mon Sep 17 00:00:00 2001 From: Dmitry Kalinin Date: Fri, 6 Mar 2020 16:38:15 +0300 Subject: [PATCH] Added tag support in new UI (without canvas drawing) --- cvat-canvas/src/typescript/canvasView.ts | 44 +++++++++++------- cvat-core/src/annotations-collection.js | 12 +++-- cvat-ui/src/actions/annotation-actions.ts | 38 ++++++++------- .../objects-side-bar/object-item.tsx | 25 +++++++++- .../controls-side-bar/setup-tag-popover.tsx | 46 ++++++++++++++----- .../objects-side-bar/object-item.tsx | 21 +++++++++ cvat-ui/src/reducers/annotation-reducer.ts | 18 +------- cvat-ui/src/reducers/interfaces.ts | 1 - 8 files changed, 137 insertions(+), 68 deletions(-) diff --git a/cvat-canvas/src/typescript/canvasView.ts b/cvat-canvas/src/typescript/canvasView.ts index 63b26e1d..846ac454 100644 --- a/cvat-canvas/src/typescript/canvasView.ts +++ b/cvat-canvas/src/typescript/canvasView.ts @@ -399,8 +399,11 @@ export class CanvasViewImpl implements CanvasView, Listener { this.svgTexts[state.clientID].remove(); } - this.svgShapes[state.clientID].off('click.canvas'); - this.svgShapes[state.clientID].remove(); + const shape = this.svgShapes[state.clientID]; + if (shape) { + shape.off('click.canvas'); + shape.remove(); + } delete this.drawnStates[state.clientID]; } @@ -847,7 +850,7 @@ export class CanvasViewImpl implements CanvasView, Listener { hidden: state.hidden, lock: state.lock, shapeType: state.shapeType, - points: [...state.points], + points: Array.isArray(state.points) ? [...state.points] : [], attributes: { ...state.attributes }, zOrder: state.zOrder, pinned: state.pinned, @@ -892,7 +895,7 @@ export class CanvasViewImpl implements CanvasView, Listener { this.activate(activeElement); } - if (state.points + if (state.points && state.points .some((p: number, id: number): boolean => p !== drawnState.points[id]) ) { const translatedPoints: number[] = translate(state.points); @@ -1021,22 +1024,24 @@ export class CanvasViewImpl implements CanvasView, Listener { const drawnState = this.drawnStates[clientID]; const shape = this.svgShapes[clientID]; - shape.removeClass('cvat_canvas_shape_activated'); + if (shape) { + shape.removeClass('cvat_canvas_shape_activated'); - if (!drawnState.pinned) { - (shape as any).off('dragstart'); - (shape as any).off('dragend'); - (shape as any).draggable(false); - } + if (!drawnState.pinned) { + (shape as any).off('dragstart'); + (shape as any).off('dragend'); + (shape as any).draggable(false); + } - if (drawnState.shapeType !== 'points') { - this.selectize(false, shape); - } + if (drawnState.shapeType !== 'points') { + this.selectize(false, shape); + } - (shape as any).off('resizestart'); - (shape as any).off('resizing'); - (shape as any).off('resizedone'); - (shape as any).resize(false); + (shape as any).off('resizestart'); + (shape as any).off('resizing'); + (shape as any).off('resizedone'); + (shape as any).resize(false); + } // TODO: Hide text only if it is hidden by settings const text = this.svgTexts[clientID]; @@ -1085,6 +1090,11 @@ export class CanvasViewImpl implements CanvasView, Listener { this.activeElement = { ...activeElement }; const shape = this.svgShapes[clientID]; + + if (!shape) { + return; + } + let text = this.svgTexts[clientID]; if (!text) { text = this.addText(state); diff --git a/cvat-core/src/annotations-collection.js b/cvat-core/src/annotations-collection.js index 759980db..0c5b35fd 100644 --- a/cvat-core/src/annotations-collection.js +++ b/cvat-core/src/annotations-collection.js @@ -810,11 +810,13 @@ 'The object has not been saved yet. Call annotations.put([state]) before', ); } - - const distance = object.constructor.distance(state.points, x, y); - if (distance !== null && (minimumDistance === null || distance < minimumDistance)) { - minimumDistance = distance; - minimumState = state; + if (!(object instanceof Tag)) { + const distance = object.constructor.distance(state.points, x, y); + if (distance !== null && (minimumDistance === null + || distance < minimumDistance)) { + minimumDistance = distance; + minimumState = state; + } } } diff --git a/cvat-ui/src/actions/annotation-actions.ts b/cvat-ui/src/actions/annotation-actions.ts index b3d0eabf..9a265752 100644 --- a/cvat-ui/src/actions/annotation-actions.ts +++ b/cvat-ui/src/actions/annotation-actions.ts @@ -85,7 +85,6 @@ export enum AnnotationActionTypes { EDIT_SHAPE = 'EDIT_SHAPE', DRAW_SHAPE = 'DRAW_SHAPE', SHAPE_DRAWN = 'SHAPE_DRAWN', - SETUP_TAG = 'SETUP_TAG', RESET_CANVAS = 'RESET_CANVAS', UPDATE_ANNOTATIONS_SUCCESS = 'UPDATE_ANNOTATIONS_SUCCESS', UPDATE_ANNOTATIONS_FAILED = 'UPDATE_ANNOTATIONS_FAILED', @@ -526,10 +525,31 @@ export function editShape(enabled: boolean): AnyAction { } export function copyShape(objectState: any): AnyAction { + const state = getStore().getState(); + + state.annotation.canvas.instance.cancel(); + if (objectState.objectType !== ObjectType.TAG) { + state.annotation.canvas.instance.draw({ + enabled: true, + initialState: objectState, + }); + } + + let activeControl = ActiveControl.CURSOR; + if (objectState.shapeType === ShapeType.RECTANGLE) { + activeControl = ActiveControl.DRAW_RECTANGLE; + } else if (objectState.shapeType === ShapeType.POINTS) { + activeControl = ActiveControl.DRAW_POINTS; + } else if (objectState.shapeType === ShapeType.POLYGON) { + activeControl = ActiveControl.DRAW_POLYGON; + } else if (objectState.shapeType === ShapeType.POLYLINE) { + activeControl = ActiveControl.DRAW_POLYLINE; + } + return { type: AnnotationActionTypes.COPY_SHAPE, payload: { - objectState, + activeControl, }, }; } @@ -856,20 +876,6 @@ export function drawShape( }; } -export function setupTag( - labelID: number, - objectType: ObjectType, -): AnyAction { - return { - type: AnnotationActionTypes.SETUP_TAG, - payload: { - labelID, - objectType, - activeControl: ActiveControl.SETUP_TAG, - }, - }; -} - export function shapeDrawn(): AnyAction { return { type: AnnotationActionTypes.SHAPE_DRAWN, diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx index 284fc613..ce0f69db 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/objects-side-bar/object-item.tsx @@ -43,6 +43,7 @@ import { function ItemMenu( serverID: number | undefined, locked: boolean, + objectType: ObjectType, copy: (() => void), remove: (() => void), propagate: (() => void), @@ -68,13 +69,13 @@ function ItemMenu( - - @@ -109,6 +110,7 @@ interface ItemTopComponentProps { serverID: number | undefined; labelID: number; labels: any[]; + objectType: ObjectType; type: string; locked: boolean; changeLabel(labelID: string): void; @@ -126,6 +128,7 @@ function ItemTopComponent(props: ItemTopComponentProps): JSX.Element { serverID, labelID, labels, + objectType, type, locked, changeLabel, @@ -159,6 +162,7 @@ function ItemTopComponent(props: ItemTopComponentProps): JSX.Element { overlay={ItemMenu( serverID, locked, + objectType, copy, remove, propagate, @@ -302,6 +306,22 @@ function ItemButtonsComponent(props: ItemButtonsComponentProps): JSX.Element { ); } + if (objectType === ObjectType.TAG) { + return ( + + + + + { locked + ? + : } + + + + + ); + } + return ( @@ -726,6 +746,7 @@ function ObjectItemComponent(props: Props): JSX.Element { clientID={clientID} labelID={labelID} labels={labels} + objectType={objectType} type={type} locked={locked} changeLabel={changeLabel} diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/setup-tag-popover.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/setup-tag-popover.tsx index 5714bc0a..03e9bff5 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/setup-tag-popover.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/controls-side-bar/setup-tag-popover.tsx @@ -11,28 +11,28 @@ import { } from 'reducers/interfaces'; import { - setupTag + createAnnotationsAsync, } from 'actions/annotation-actions'; import { Canvas } from 'cvat-canvas'; import SetupTagPopoverComponent from 'components/annotation-page/standard-workspace/controls-side-bar/setup-tag-popover'; +import getCore from 'cvat-core'; +const cvat = getCore(); interface DispatchToProps { - onTagSetup( - labelID: number, - ): void; + onCreateAnnotations(sessionInstance: any, frame: number, states: any[]): void; } interface StateToProps { canvasInstance: Canvas; + jobInstance: any; labels: any[]; + frame: number; } function mapDispatchToProps(dispatch: any): DispatchToProps { return { - onTagSetup( - labelID: number, - ): void { - dispatch(setupTag(labelID, ObjectType.TAG)); + onCreateAnnotations(sessionInstance: any, frame: number, states: any[]): void { + dispatch(createAnnotationsAsync(sessionInstance, frame, states)); }, }; } @@ -45,13 +45,21 @@ function mapStateToProps(state: CombinedState): StateToProps { }, job: { labels, + instance: jobInstance, + }, + player: { + frame: { + number: frame, + }, }, }, } = state; return { canvasInstance, + jobInstance, labels, + frame, }; } @@ -77,11 +85,27 @@ class DrawShapePopoverContainer extends React.PureComponent { }); }; - private onSetup(labelID: number): void { - const { canvasInstance, onTagSetup } = this.props; + private onSetup(): void { + const { + canvasInstance, + onCreateAnnotations, + jobInstance, + frame, + } = this.props; + + const { selectedLabelID } = this.state; canvasInstance.cancel(); - onTagSetup(labelID); + + const state = { + objectType: ObjectType.TAG, + label: jobInstance.task.labels + .filter((label: any) => label.id === selectedLabelID)[0], + frame, + + }; + const objectState = new cvat.classes.ObjectState(state); + onCreateAnnotations(jobInstance, frame, [objectState]); } public render(): JSX.Element { diff --git a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx index 6ced5702..0723f438 100644 --- a/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx +++ b/cvat-ui/src/containers/annotation-page/standard-workspace/objects-side-bar/object-item.tsx @@ -9,10 +9,12 @@ import { ActiveControl, CombinedState, ColorBy, + ObjectType, } from 'reducers/interfaces'; import { collapseObjectItems, changeLabelColorAsync, + createAnnotationsAsync, updateAnnotationsAsync, changeFrameAsync, removeObjectAsync, @@ -23,6 +25,9 @@ import { } from 'actions/annotation-actions'; import ObjectStateItemComponent from 'components/annotation-page/standard-workspace/objects-side-bar/object-item'; +import getCore from 'cvat-core'; + +const cvat = getCore(); interface OwnProps { clientID: number; @@ -47,6 +52,7 @@ interface StateToProps { interface DispatchToProps { changeFrame(frame: number): void; updateState(sessionInstance: any, frameNumber: number, objectState: any): void; + createAnnotations(sessionInstance: any, frameNumber: number, state: any): void collapseOrExpand(objectStates: any[], collapsed: boolean): void; activateObject: (activatedStateID: number | null) => void; removeObject: (sessionInstance: any, objectState: any) => void; @@ -123,6 +129,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps { updateState(sessionInstance: any, frameNumber: number, state: any): void { dispatch(updateAnnotationsAsync(sessionInstance, frameNumber, [state])); }, + createAnnotations(sessionInstance: any, frameNumber: number, state: any): void { + dispatch(createAnnotationsAsync(sessionInstance, frameNumber, state)); + }, collapseOrExpand(objectStates: any[], collapsed: boolean): void { dispatch(collapseObjectItems(objectStates, collapsed)); }, @@ -215,9 +224,21 @@ class ObjectItemContainer extends React.PureComponent { const { objectState, copyShape, + jobInstance, + frameNumber, + createAnnotations, } = this.props; copyShape(objectState); + if (objectState.objectType === ObjectType.TAG) { + const state = new cvat.classes.ObjectState({ + objectType: objectState.objectType, + label: objectState.label, + frame: frameNumber, + + }); + createAnnotations(jobInstance, frameNumber, [state]); + } }; private propagate = (): void => { diff --git a/cvat-ui/src/reducers/annotation-reducer.ts b/cvat-ui/src/reducers/annotation-reducer.ts index 27697cb5..3a8776ae 100644 --- a/cvat-ui/src/reducers/annotation-reducer.ts +++ b/cvat-ui/src/reducers/annotation-reducer.ts @@ -429,6 +429,7 @@ export default (state = defaultState, action: AnyAction): AnnotationState => { ...state, annotations: { ...state.annotations, + activatedStateID: null, }, canvas: { ...state.canvas, @@ -678,24 +679,9 @@ export default (state = defaultState, action: AnyAction): AnnotationState => { } case AnnotationActionTypes.COPY_SHAPE: { const { - objectState, + activeControl, } = action.payload; - state.canvas.instance.cancel(); - state.canvas.instance.draw({ - enabled: true, - initialState: objectState, - }); - - let activeControl = ActiveControl.DRAW_RECTANGLE; - if (objectState.shapeType === ShapeType.POINTS) { - activeControl = ActiveControl.DRAW_POINTS; - } else if (objectState.shapeType === ShapeType.POLYGON) { - activeControl = ActiveControl.DRAW_POLYGON; - } else if (objectState.shapeType === ShapeType.POLYLINE) { - activeControl = ActiveControl.DRAW_POLYLINE; - } - return { ...state, canvas: { diff --git a/cvat-ui/src/reducers/interfaces.ts b/cvat-ui/src/reducers/interfaces.ts index 0fab4ef5..644d85d5 100644 --- a/cvat-ui/src/reducers/interfaces.ts +++ b/cvat-ui/src/reducers/interfaces.ts @@ -245,7 +245,6 @@ export enum ActiveControl { DRAW_POLYGON = 'draw_polygon', DRAW_POLYLINE = 'draw_polyline', DRAW_POINTS = 'draw_points', - SETUP_TAG = 'setup_tag', MERGE = 'merge', GROUP = 'group', SPLIT = 'split',