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(
-
-
+
To foreground
@@ -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',