React UI: Batch of fixes (#1211)

* Disabled tracks for polyshapes in UI

* RectDrawingMethod enum pushed to cvat-canvas, fixed some code issues

* Optional arguments

* Draw a text for locked shapes, some fixes with not keyframe shapes

* Fixed zooming & batch grouping

* Reset zoom for tasks with images

* Fixed putting shapes out of canvas

* Fixed grid opacity, little refactoring of componentDidUpdate in canvas-wrapper component

* Fixed corner cases for drawing

* Fixed putting shapes out of canvas

* Improved drawing

* Removed extra event handler
main
Boris Sekachev 6 years ago committed by GitHub
parent 5659a0a0e1
commit 5dc52f94a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -37,10 +37,15 @@ Canvas itself handles:
CLOCKWISE90, CLOCKWISE90,
} }
enum RectDrawingMethod {
CLASSIC = 'By 2 points',
EXTREME_POINTS = 'By 4 points'
}
interface DrawData { interface DrawData {
enabled: boolean; enabled: boolean;
shapeType?: string; shapeType?: string;
rectDrawingMethod?: string; rectDrawingMethod?: RectDrawingMethod;
numberOfPoints?: number; numberOfPoints?: number;
initialState?: any; initialState?: any;
crosshair?: boolean; crosshair?: boolean;
@ -147,6 +152,7 @@ Standard JS events are used.
enabled: true, enabled: true,
shapeType: 'rectangle', shapeType: 'rectangle',
crosshair: true, crosshair: true,
rectDrawingMethod: window.Canvas.RectDrawingMethod.CLASSIC,
}); });
``` ```

@ -10,6 +10,7 @@ import {
GroupData, GroupData,
CanvasModel, CanvasModel,
CanvasModelImpl, CanvasModelImpl,
RectDrawingMethod,
} from './canvasModel'; } from './canvasModel';
import { import {
@ -141,4 +142,5 @@ export {
CanvasImpl as Canvas, CanvasImpl as Canvas,
Rotation, Rotation,
CanvasVersion, CanvasVersion,
RectDrawingMethod,
}; };

@ -4,7 +4,6 @@
import { MasterImpl } from './master'; import { MasterImpl } from './master';
export interface Size { export interface Size {
width: number; width: number;
height: number; height: number;
@ -36,10 +35,15 @@ export interface ActiveElement {
attributeID: number | null; attributeID: number | null;
} }
export enum RectDrawingMethod {
CLASSIC = 'By 2 points',
EXTREME_POINTS = 'By 4 points'
}
export interface DrawData { export interface DrawData {
enabled: boolean; enabled: boolean;
shapeType?: string; shapeType?: string;
rectDrawingMethod?: string; rectDrawingMethod?: RectDrawingMethod;
numberOfPoints?: number; numberOfPoints?: number;
initialState?: any; initialState?: any;
crosshair?: boolean; crosshair?: boolean;

@ -106,11 +106,10 @@ export class CanvasViewImpl implements CanvasView, Listener {
this.geometry, this.geometry,
); );
} else { } else {
this.mode = Mode.IDLE;
this.controller.draw({ this.controller.draw({
enabled: false, enabled: false,
}); });
this.mode = Mode.IDLE;
} }
} }
@ -551,7 +550,8 @@ export class CanvasViewImpl implements CanvasView, Listener {
this.grid.setAttribute('version', '2'); this.grid.setAttribute('version', '2');
this.gridPath.setAttribute('d', 'M 1000 0 L 0 0 0 1000'); this.gridPath.setAttribute('d', 'M 1000 0 L 0 0 0 1000');
this.gridPath.setAttribute('fill', 'none'); this.gridPath.setAttribute('fill', 'none');
this.gridPath.setAttribute('stroke-width', '1.5'); this.gridPath.setAttribute('stroke-width', `${consts.BASE_GRID_WIDTH}`);
this.gridPath.setAttribute('opacity', 'inherit');
this.gridPattern.setAttribute('id', 'cvat_canvas_grid_pattern'); this.gridPattern.setAttribute('id', 'cvat_canvas_grid_pattern');
this.gridPattern.setAttribute('width', '100'); this.gridPattern.setAttribute('width', '100');
this.gridPattern.setAttribute('height', '100'); this.gridPattern.setAttribute('height', '100');
@ -626,7 +626,9 @@ export class CanvasViewImpl implements CanvasView, Listener {
this.content.addEventListener('mousedown', (event): void => { this.content.addEventListener('mousedown', (event): void => {
if ([1, 2].includes(event.which)) { if ([1, 2].includes(event.which)) {
self.controller.enableDrag(event.clientX, event.clientY); if (![Mode.ZOOM_CANVAS, Mode.GROUP].includes(this.mode) || event.which === 2) {
self.controller.enableDrag(event.clientX, event.clientY);
}
event.preventDefault(); event.preventDefault();
} }
}); });
@ -760,13 +762,16 @@ export class CanvasViewImpl implements CanvasView, Listener {
} }
} else if (reason === UpdateReasons.DRAW) { } else if (reason === UpdateReasons.DRAW) {
const data: DrawData = this.controller.drawData; const data: DrawData = this.controller.drawData;
if (data.enabled) { if (data.enabled && this.mode === Mode.IDLE) {
this.canvas.style.cursor = 'crosshair'; this.canvas.style.cursor = 'crosshair';
this.mode = Mode.DRAW; this.mode = Mode.DRAW;
this.drawHandler.draw(data, this.geometry);
} else { } else {
this.canvas.style.cursor = ''; this.canvas.style.cursor = '';
if (this.mode !== Mode.IDLE) {
this.drawHandler.draw(data, this.geometry);
}
} }
this.drawHandler.draw(data, this.geometry);
} else if (reason === UpdateReasons.MERGE) { } else if (reason === UpdateReasons.MERGE) {
const data: MergeData = this.controller.mergeData; const data: MergeData = this.controller.mergeData;
if (data.enabled) { if (data.enabled) {
@ -1060,24 +1065,18 @@ export class CanvasViewImpl implements CanvasView, Listener {
const [state] = this.controller.objects const [state] = this.controller.objects
.filter((_state: any): boolean => _state.clientID === clientID); .filter((_state: any): boolean => _state.clientID === clientID);
if (!state) { if (state && state.shapeType === 'points') {
return;
}
if (state.shapeType === 'points') {
this.svgShapes[clientID].remember('_selectHandler').nested this.svgShapes[clientID].remember('_selectHandler').nested
.style('pointer-events', state.lock ? 'none' : ''); .style('pointer-events', state.lock ? 'none' : '');
} }
if (state.hidden || state.lock) { if (!state || state.hidden || state.outside) {
return; return;
} }
this.activeElement = { ...activeElement }; this.activeElement = { ...activeElement };
const shape = this.svgShapes[clientID]; const shape = this.svgShapes[clientID];
shape.addClass('cvat_canvas_shape_activated');
let text = this.svgTexts[clientID]; let text = this.svgTexts[clientID];
// Draw text if it's hidden by default
if (!text) { if (!text) {
text = this.addText(state); text = this.addText(state);
this.svgTexts[state.clientID] = text; this.svgTexts[state.clientID] = text;
@ -1087,7 +1086,11 @@ export class CanvasViewImpl implements CanvasView, Listener {
); );
} }
const self = this; if (state.lock) {
return;
}
shape.addClass('cvat_canvas_shape_activated');
if (state.shapeType === 'points') { if (state.shapeType === 'points') {
this.content.append(this.svgShapes[clientID] this.content.append(this.svgShapes[clientID]
.remember('_selectHandler').nested.node); .remember('_selectHandler').nested.node);
@ -1103,7 +1106,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
}).on('dragend', (e: CustomEvent): void => { }).on('dragend', (e: CustomEvent): void => {
if (text) { if (text) {
text.removeClass('cvat_canvas_hidden'); text.removeClass('cvat_canvas_hidden');
self.updateTextPosition( this.updateTextPosition(
text, text,
shape, shape,
); );
@ -1122,6 +1125,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
+ `${shape.attr('y') + shape.attr('height')}`, + `${shape.attr('y') + shape.attr('height')}`,
).map((x: number): number => x - offset); ).map((x: number): number => x - offset);
this.drawnStates[state.clientID].points = points;
this.onEditDone(state, points); this.onEditDone(state, points);
} }
}); });
@ -1153,7 +1157,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
if (text) { if (text) {
text.removeClass('cvat_canvas_hidden'); text.removeClass('cvat_canvas_hidden');
self.updateTextPosition( this.updateTextPosition(
text, text,
shape, shape,
); );
@ -1170,6 +1174,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
+ `${shape.attr('y') + shape.attr('height')}`, + `${shape.attr('y') + shape.attr('height')}`,
).map((x: number): number => x - offset); ).map((x: number): number => x - offset);
this.drawnStates[state.clientID].points = points;
this.onEditDone(state, points); this.onEditDone(state, points);
} }
}); });

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
const BASE_STROKE_WIDTH = 1.75; const BASE_STROKE_WIDTH = 1.75;
const BASE_GRID_WIDTH = 1; const BASE_GRID_WIDTH = 2;
const BASE_POINT_SIZE = 5; const BASE_POINT_SIZE = 5;
const TEXT_MARGIN = 10; const TEXT_MARGIN = 10;
const AREA_THRESHOLD = 9; const AREA_THRESHOLD = 9;

@ -10,6 +10,7 @@ import './svg.patch';
import { import {
DrawData, DrawData,
Geometry, Geometry,
RectDrawingMethod,
} from './canvasModel'; } from './canvasModel';
import { import {
@ -43,6 +44,7 @@ export class DrawHandlerImpl implements DrawHandler {
// we should use any instead of SVG.Shape because svg plugins cannot change declared interface // we should use any instead of SVG.Shape because svg plugins cannot change declared interface
// so, methods like draw() just undefined for SVG.Shape, but nevertheless they exist // so, methods like draw() just undefined for SVG.Shape, but nevertheless they exist
private drawInstance: any; private drawInstance: any;
private initialized: boolean;
private pointsGroup: SVG.G | null; private pointsGroup: SVG.G | null;
private shapeSizeElement: ShapeSizeElement; private shapeSizeElement: ShapeSizeElement;
@ -113,6 +115,12 @@ export class DrawHandlerImpl implements DrawHandler {
} }
private release(): void { private release(): void {
if (!this.initialized) {
// prevents recursive calls
return;
}
this.initialized = false;
this.canvas.off('mousedown.draw'); this.canvas.off('mousedown.draw');
this.canvas.off('mouseup.draw'); this.canvas.off('mouseup.draw');
this.canvas.off('mousemove.draw'); this.canvas.off('mousemove.draw');
@ -123,18 +131,21 @@ export class DrawHandlerImpl implements DrawHandler {
this.pointsGroup = null; this.pointsGroup = null;
} }
if (this.drawInstance) { // Draw plugin in some cases isn't activated
// Draw plugin isn't activated when draw from initialState // For example when draw from initialState
// So, we don't need to use any draw events // Or when no drawn points, but we call cancel() drawing
if (!this.drawData.initialState) { // We check if it is activated with remember function
this.drawInstance.off('drawdone'); if (this.drawInstance.remember('_paintHandler')) {
this.drawInstance.off('drawstop'); if (this.drawData.shapeType !== 'rectangle') {
this.drawInstance.draw('stop'); // Check for unsaved drawn shapes
this.drawInstance.draw('done');
} }
// Clear drawing
this.drawInstance.remove(); this.drawInstance.draw('stop');
this.drawInstance = null;
} }
this.drawInstance.off();
this.drawInstance.remove();
this.drawInstance = null;
if (this.shapeSizeElement) { if (this.shapeSizeElement) {
this.shapeSizeElement.rm(); this.shapeSizeElement.rm();
@ -152,45 +163,19 @@ export class DrawHandlerImpl implements DrawHandler {
} }
} }
private closeDrawing(): void {
if (this.drawInstance) {
// Draw plugin isn't activated when draw from initialState
// So, we don't need to use any draw events
if (!this.drawData.initialState) {
const { drawInstance } = this;
this.drawInstance = null;
if (this.drawData.shapeType === 'rectangle'
&& this.drawData.rectDrawingMethod !== 'by_four_points') {
drawInstance.draw('cancel');
} else {
drawInstance.draw('done');
}
this.drawInstance = drawInstance;
this.release();
} else {
this.release();
this.onDrawDone(null);
}
// here is a cycle
// onDrawDone => controller => model => view => closeDrawing
// one call of closeDrawing is unuseful, but it's okey
}
}
private drawBox(): void { private drawBox(): void {
this.drawInstance = this.canvas.rect(); this.drawInstance = this.canvas.rect();
this.drawInstance.on('drawstop', (e: Event): void => { this.drawInstance.on('drawstop', (e: Event): void => {
const bbox = (e.target as SVGRectElement).getBBox(); const bbox = (e.target as SVGRectElement).getBBox();
const [xtl, ytl, xbr, ybr] = this.getFinalRectCoordinates(bbox); const [xtl, ytl, xbr, ybr] = this.getFinalRectCoordinates(bbox);
const { shapeType } = this.drawData;
this.cancel();
if ((xbr - xtl) * (ybr - ytl) >= consts.AREA_THRESHOLD) { if ((xbr - xtl) * (ybr - ytl) >= consts.AREA_THRESHOLD) {
this.onDrawDone({ this.onDrawDone({
shapeType: this.drawData.shapeType, shapeType,
points: [xtl, ytl, xbr, ybr], points: [xtl, ytl, xbr, ybr],
}); });
} else {
this.onDrawDone(null);
} }
}).on('drawupdate', (): void => { }).on('drawupdate', (): void => {
this.shapeSizeElement.update(this.drawInstance); this.shapeSizeElement.update(this.drawInstance);
@ -206,10 +191,10 @@ export class DrawHandlerImpl implements DrawHandler {
.addClass('cvat_canvas_shape_drawing').attr({ .addClass('cvat_canvas_shape_drawing').attr({
'stroke-width': 0, 'stroke-width': 0,
opacity: 0, opacity: 0,
}).on('drawstart', () => { }).on('drawstart', (): void => {
// init numberOfPoints as one on drawstart // init numberOfPoints as one on drawstart
numberOfPoints = 1; numberOfPoints = 1;
}).on('drawpoint', (e: CustomEvent) => { }).on('drawpoint', (e: CustomEvent): void => {
// increase numberOfPoints by one on drawpoint // increase numberOfPoints by one on drawpoint
numberOfPoints += 1; numberOfPoints += 1;
@ -217,23 +202,20 @@ export class DrawHandlerImpl implements DrawHandler {
if (numberOfPoints === 4) { if (numberOfPoints === 4) {
const bbox = (e.target as SVGPolylineElement).getBBox(); const bbox = (e.target as SVGPolylineElement).getBBox();
const [xtl, ytl, xbr, ybr] = this.getFinalRectCoordinates(bbox); const [xtl, ytl, xbr, ybr] = this.getFinalRectCoordinates(bbox);
const { shapeType } = this.drawData;
this.cancel();
if ((xbr - xtl) * (ybr - ytl) >= consts.AREA_THRESHOLD) { if ((xbr - xtl) * (ybr - ytl) >= consts.AREA_THRESHOLD) {
this.onDrawDone({ this.onDrawDone({
shapeType: this.drawData.shapeType, shapeType,
points: [xtl, ytl, xbr, ybr], points: [xtl, ytl, xbr, ybr],
}); });
} else {
this.onDrawDone(null);
} }
} }
}).on('undopoint', () => { }).on('undopoint', (): void => {
if (numberOfPoints > 0) { if (numberOfPoints > 0) {
numberOfPoints -= 1; numberOfPoints -= 1;
} }
}).off('drawdone').on('drawdone', () => {
// close drawing mode without drawing rect
this.onDrawDone(null);
}); });
this.drawPolyshape(); this.drawPolyshape();
@ -308,34 +290,31 @@ export class DrawHandlerImpl implements DrawHandler {
this.drawInstance.on('drawdone', (e: CustomEvent): void => { this.drawInstance.on('drawdone', (e: CustomEvent): void => {
const targetPoints = pointsToArray((e.target as SVGElement).getAttribute('points')); const targetPoints = pointsToArray((e.target as SVGElement).getAttribute('points'));
const { const { points, box } = this.getFinalPolyshapeCoordinates(targetPoints);
points, const { shapeType } = this.drawData;
box, this.cancel();
} = this.getFinalPolyshapeCoordinates(targetPoints);
if (this.drawData.shapeType === 'polygon' if (shapeType === 'polygon'
&& ((box.xbr - box.xtl) * (box.ybr - box.ytl) >= consts.AREA_THRESHOLD) && ((box.xbr - box.xtl) * (box.ybr - box.ytl) >= consts.AREA_THRESHOLD)
&& points.length >= 3 * 2) { && points.length >= 3 * 2) {
this.onDrawDone({ this.onDrawDone({
shapeType: this.drawData.shapeType, shapeType,
points, points,
}); });
} else if (this.drawData.shapeType === 'polyline' } else if (shapeType === 'polyline'
&& ((box.xbr - box.xtl) >= consts.SIZE_THRESHOLD && ((box.xbr - box.xtl) >= consts.SIZE_THRESHOLD
|| (box.ybr - box.ytl) >= consts.SIZE_THRESHOLD) || (box.ybr - box.ytl) >= consts.SIZE_THRESHOLD)
&& points.length >= 2 * 2) { && points.length >= 2 * 2) {
this.onDrawDone({ this.onDrawDone({
shapeType: this.drawData.shapeType, shapeType,
points, points,
}); });
} else if (this.drawData.shapeType === 'points' } else if (shapeType === 'points'
&& (e.target as any).getAttribute('points') !== '0,0') { && (e.target as any).getAttribute('points') !== '0,0') {
this.onDrawDone({ this.onDrawDone({
shapeType: this.drawData.shapeType, shapeType,
points, points,
}); });
} else {
this.onDrawDone(null);
} }
}); });
} }
@ -578,7 +557,7 @@ export class DrawHandlerImpl implements DrawHandler {
this.setupPasteEvents(); this.setupPasteEvents();
} else { } else {
if (this.drawData.shapeType === 'rectangle') { if (this.drawData.shapeType === 'rectangle') {
if (this.drawData.rectDrawingMethod === 'by_four_points') { if (this.drawData.rectDrawingMethod === RectDrawingMethod.EXTREME_POINTS) {
// draw box by extreme clicking // draw box by extreme clicking
this.drawBoxBy4Points(); this.drawBoxBy4Points();
} else { } else {
@ -596,6 +575,8 @@ export class DrawHandlerImpl implements DrawHandler {
} }
this.setupDrawEvents(); this.setupDrawEvents();
} }
this.initialized = true;
} }
public constructor( public constructor(
@ -606,6 +587,7 @@ export class DrawHandlerImpl implements DrawHandler {
this.onDrawDone = onDrawDone; this.onDrawDone = onDrawDone;
this.canvas = canvas; this.canvas = canvas;
this.text = text; this.text = text;
this.initialized = false;
this.drawData = null; this.drawData = null;
this.geometry = null; this.geometry = null;
this.crosshair = null; this.crosshair = null;
@ -686,7 +668,7 @@ export class DrawHandlerImpl implements DrawHandler {
this.initDrawing(); this.initDrawing();
this.startDraw(); this.startDraw();
} else { } else {
this.closeDrawing(); this.cancel();
this.drawData = drawData; this.drawData = drawData;
} }
} }
@ -694,8 +676,5 @@ export class DrawHandlerImpl implements DrawHandler {
public cancel(): void { public cancel(): void {
this.release(); this.release();
this.onDrawDone(null); this.onDrawDone(null);
// here is a cycle
// onDrawDone => controller => model => view => closeDrawing
// one call of closeDrawing is unuseful, but it's okey
} }
} }

@ -20,6 +20,7 @@ import {
} from 'reducers/interfaces'; } from 'reducers/interfaces';
import getCore from 'cvat-core'; import getCore from 'cvat-core';
import { RectDrawingMethod } from 'cvat-canvas';
import { getCVATStore } from 'cvat-store'; import { getCVATStore } from 'cvat-store';
const cvat = getCore(); const cvat = getCore();
@ -807,7 +808,7 @@ export function drawShape(
labelID: number, labelID: number,
objectType: ObjectType, objectType: ObjectType,
points?: number, points?: number,
rectDrawingMethod?: string, rectDrawingMethod?: RectDrawingMethod,
): AnyAction { ): AnyAction {
let activeControl = ActiveControl.DRAW_RECTANGLE; let activeControl = ActiveControl.DRAW_RECTANGLE;
if (shapeType === ShapeType.POLYGON) { if (shapeType === ShapeType.POLYGON) {

@ -100,10 +100,6 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
colorBy, colorBy,
selectedOpacity, selectedOpacity,
blackBorders, blackBorders,
grid,
gridSize,
gridColor,
gridOpacity,
frameData, frameData,
annotations, annotations,
canvasInstance, canvasInstance,
@ -122,34 +118,12 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
} }
} }
if (prevProps.grid !== grid) {
const gridElement = window.document.getElementById('cvat_canvas_grid');
if (gridElement) {
gridElement.style.display = grid ? 'block' : 'none';
}
}
if (prevProps.gridSize !== gridSize) {
canvasInstance.grid(gridSize, gridSize);
}
if (prevProps.gridColor !== gridColor) {
const gridPattern = window.document.getElementById('cvat_canvas_grid_pattern');
if (gridPattern) {
gridPattern.style.stroke = gridColor.toLowerCase();
}
}
if (prevProps.gridOpacity !== gridOpacity) {
const gridPattern = window.document.getElementById('cvat_canvas_grid_pattern');
if (gridPattern) {
gridPattern.style.opacity = `${gridOpacity / 100}`;
}
}
if (prevProps.activatedStateID !== null if (prevProps.activatedStateID !== null
&& prevProps.activatedStateID !== activatedStateID) { && prevProps.activatedStateID !== activatedStateID) {
canvasInstance.activate(null); canvasInstance.activate(null);
}
if (activatedStateID) {
const el = window.document.getElementById(`cvat_canvas_shape_${prevProps.activatedStateID}`); const el = window.document.getElementById(`cvat_canvas_shape_${prevProps.activatedStateID}`);
if (el) { if (el) {
(el as any).instance.fill({ opacity: opacity / 100 }); (el as any).instance.fill({ opacity: opacity / 100 });
@ -160,17 +134,17 @@ export default class CanvasWrapperComponent extends React.PureComponent<Props> {
this.updateCanvas(); this.updateCanvas();
} }
if (prevProps.opacity !== opacity || prevProps.blackBorders !== blackBorders
|| prevProps.selectedOpacity !== selectedOpacity || prevProps.colorBy !== colorBy) {
this.updateShapesView();
}
if (prevProps.frame !== frameData.number && resetZoom) { if (prevProps.frame !== frameData.number && resetZoom) {
canvasInstance.html().addEventListener('canvas.setup', () => { canvasInstance.html().addEventListener('canvas.setup', () => {
canvasInstance.fit(); canvasInstance.fit();
}, { once: true }); }, { once: true });
} }
if (prevProps.opacity !== opacity || prevProps.blackBorders !== blackBorders
|| prevProps.selectedOpacity !== selectedOpacity || prevProps.colorBy !== colorBy) {
this.updateShapesView();
}
if (prevProps.curZLayer !== curZLayer) { if (prevProps.curZLayer !== curZLayer) {
canvasInstance.setZLayer(curZLayer); canvasInstance.setZLayer(curZLayer);
} }

@ -16,16 +16,14 @@ import {
import { RadioChangeEvent } from 'antd/lib/radio'; import { RadioChangeEvent } from 'antd/lib/radio';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import { import { RectDrawingMethod } from 'cvat-canvas';
ShapeType, import { ShapeType } from 'reducers/interfaces';
RectDrawingMethod,
} from 'reducers/interfaces';
interface Props { interface Props {
shapeType: ShapeType; shapeType: ShapeType;
rectDrawingMethod: RectDrawingMethod;
labels: any[]; labels: any[];
minimumPoints: number; minimumPoints: number;
rectDrawingMethod?: RectDrawingMethod;
numberOfPoints?: number; numberOfPoints?: number;
selectedLabeID: number; selectedLabeID: number;
onChangeLabel(value: string): void; onChangeLabel(value: string): void;
@ -42,6 +40,7 @@ function DrawShapePopoverComponent(props: Props): JSX.Element {
minimumPoints, minimumPoints,
selectedLabeID, selectedLabeID,
numberOfPoints, numberOfPoints,
rectDrawingMethod,
onDrawTrack, onDrawTrack,
onDrawShape, onDrawShape,
onChangeLabel, onChangeLabel,
@ -92,17 +91,17 @@ function DrawShapePopoverComponent(props: Props): JSX.Element {
<Col> <Col>
<Radio.Group <Radio.Group
style={{ display: 'flex' }} style={{ display: 'flex' }}
defaultValue={RectDrawingMethod.BY_TWO_POINTS} value={rectDrawingMethod}
onChange={onChangeRectDrawingMethod} onChange={onChangeRectDrawingMethod}
> >
<Radio <Radio
value={RectDrawingMethod.BY_TWO_POINTS} value={RectDrawingMethod.CLASSIC}
style={{ width: 'auto' }} style={{ width: 'auto' }}
> >
By 2 Points By 2 Points
</Radio> </Radio>
<Radio <Radio
value={RectDrawingMethod.BY_FOUR_POINTS} value={RectDrawingMethod.EXTREME_POINTS}
style={{ width: 'auto' }} style={{ width: 'auto' }}
> >
By 4 Points By 4 Points
@ -139,6 +138,7 @@ function DrawShapePopoverComponent(props: Props): JSX.Element {
<Col span={12}> <Col span={12}>
<Button <Button
onClick={onDrawTrack} onClick={onDrawTrack}
disabled={shapeType !== ShapeType.RECTANGLE}
> >
Track Track
</Button> </Button>

@ -10,13 +10,12 @@ import {
CombinedState, CombinedState,
ShapeType, ShapeType,
ObjectType, ObjectType,
RectDrawingMethod,
} from 'reducers/interfaces'; } from 'reducers/interfaces';
import { import {
drawShape, drawShape,
} from 'actions/annotation-actions'; } from 'actions/annotation-actions';
import { Canvas } from 'cvat-canvas'; import { Canvas, RectDrawingMethod } from 'cvat-canvas';
import DrawShapePopoverComponent from 'components/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover'; import DrawShapePopoverComponent from 'components/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover';
interface OwnProps { interface OwnProps {
@ -29,7 +28,7 @@ interface DispatchToProps {
labelID: number, labelID: number,
objectType: ObjectType, objectType: ObjectType,
points?: number, points?: number,
rectDrawingMethod?: string, rectDrawingMethod?: RectDrawingMethod,
): void; ): void;
} }
@ -46,7 +45,7 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
labelID: number, labelID: number,
objectType: ObjectType, objectType: ObjectType,
points?: number, points?: number,
rectDrawingMethod?: string, rectDrawingMethod?: RectDrawingMethod,
): void { ): void {
dispatch(drawShape(shapeType, labelID, objectType, points, rectDrawingMethod)); dispatch(drawShape(shapeType, labelID, objectType, points, rectDrawingMethod));
}, },
@ -75,7 +74,7 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
type Props = StateToProps & DispatchToProps; type Props = StateToProps & DispatchToProps;
interface State { interface State {
rectDrawingMethod?: string; rectDrawingMethod?: RectDrawingMethod;
numberOfPoints?: number; numberOfPoints?: number;
selectedLabelID: number; selectedLabelID: number;
} }
@ -85,13 +84,15 @@ class DrawShapePopoverContainer extends React.PureComponent<Props, State> {
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
const { shapeType } = props;
const defaultLabelID = props.labels[0].id; const defaultLabelID = props.labels[0].id;
const defaultRectDrawingMethod = RectDrawingMethod.BY_TWO_POINTS; const defaultRectDrawingMethod = RectDrawingMethod.CLASSIC;
this.state = { this.state = {
selectedLabelID: defaultLabelID, selectedLabelID: defaultLabelID,
rectDrawingMethod: shapeType === ShapeType.RECTANGLE
? defaultRectDrawingMethod : undefined,
}; };
const { shapeType } = props;
if (shapeType === ShapeType.POLYGON) { if (shapeType === ShapeType.POLYGON) {
this.minimumPoints = 3; this.minimumPoints = 3;
} }
@ -101,9 +102,6 @@ class DrawShapePopoverContainer extends React.PureComponent<Props, State> {
if (shapeType === ShapeType.POINTS) { if (shapeType === ShapeType.POINTS) {
this.minimumPoints = 1; this.minimumPoints = 1;
} }
if (shapeType === ShapeType.RECTANGLE) {
this.state.rectDrawingMethod = defaultRectDrawingMethod;
}
} }
private onDraw(objectType: ObjectType): void { private onDraw(objectType: ObjectType): void {
@ -129,7 +127,7 @@ class DrawShapePopoverContainer extends React.PureComponent<Props, State> {
}); });
onDrawStart(shapeType, selectedLabelID, onDrawStart(shapeType, selectedLabelID,
objectType, numberOfPoints); objectType, numberOfPoints, rectDrawingMethod);
} }
private onChangeRectDrawingMethod = (event: RadioChangeEvent): void => { private onChangeRectDrawingMethod = (event: RadioChangeEvent): void => {
@ -166,6 +164,7 @@ class DrawShapePopoverContainer extends React.PureComponent<Props, State> {
public render(): JSX.Element { public render(): JSX.Element {
const { const {
rectDrawingMethod,
selectedLabelID, selectedLabelID,
numberOfPoints, numberOfPoints,
} = this.state; } = this.state;
@ -182,6 +181,7 @@ class DrawShapePopoverContainer extends React.PureComponent<Props, State> {
minimumPoints={this.minimumPoints} minimumPoints={this.minimumPoints}
selectedLabeID={selectedLabelID} selectedLabeID={selectedLabelID}
numberOfPoints={numberOfPoints} numberOfPoints={numberOfPoints}
rectDrawingMethod={rectDrawingMethod}
onChangeLabel={this.onChangeLabel} onChangeLabel={this.onChangeLabel}
onChangePoints={this.onChangePoints} onChangePoints={this.onChangePoints}
onChangeRectDrawingMethod={this.onChangeRectDrawingMethod} onChangeRectDrawingMethod={this.onChangeRectDrawingMethod}

@ -6,10 +6,12 @@ import {
Canvas, Canvas,
Rotation, Rotation,
CanvasVersion, CanvasVersion,
RectDrawingMethod,
} from '../../cvat-canvas/src/typescript/canvas'; } from '../../cvat-canvas/src/typescript/canvas';
export { export {
Canvas, Canvas,
Rotation, Rotation,
CanvasVersion, CanvasVersion,
RectDrawingMethod,
}; };

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { Canvas } from 'cvat-canvas'; import { Canvas, RectDrawingMethod } from 'cvat-canvas';
export type StringObject = { export type StringObject = {
[index: string]: string; [index: string]: string;
@ -251,11 +251,6 @@ export enum ActiveControl {
EDIT = 'edit', EDIT = 'edit',
} }
export enum RectDrawingMethod {
BY_TWO_POINTS = 'by_two_points',
BY_FOUR_POINTS = 'by_four_points'
}
export enum ShapeType { export enum ShapeType {
RECTANGLE = 'rectangle', RECTANGLE = 'rectangle',
POLYGON = 'polygon', POLYGON = 'polygon',

@ -4,6 +4,7 @@
import { AnyAction } from 'redux'; import { AnyAction } from 'redux';
import { SettingsActionTypes } from 'actions/settings-actions'; import { SettingsActionTypes } from 'actions/settings-actions';
import { AnnotationActionTypes } from 'actions/annotation-actions';
import { import {
SettingsState, SettingsState,
@ -33,7 +34,7 @@ const defaultState: SettingsState = {
grid: false, grid: false,
gridSize: 100, gridSize: 100,
gridColor: GridColor.White, gridColor: GridColor.White,
gridOpacity: 0, gridOpacity: 100,
brightnessLevel: 100, brightnessLevel: 100,
contrastLevel: 100, contrastLevel: 100,
saturationLevel: 100, saturationLevel: 100,
@ -213,6 +214,17 @@ export default (state = defaultState, action: AnyAction): SettingsState => {
}, },
}; };
} }
case AnnotationActionTypes.GET_JOB_SUCCESS: {
const { job } = action.payload;
return {
...state,
player: {
...state.player,
resetZoom: job && job.task.mode === 'annotation',
},
};
}
default: { default: {
return state; return state;
} }

Loading…
Cancel
Save