React UI: Player updates (#1058)
* Move, zoom integration * Moving, zooming, additional canvas handler * Activating & changing for objects * Improved colors * Saving annotations on the server * Fixed size * Refactoring * Added couple of notifications * Basic shape drawing * Cancel previous drawing * Refactoring * Minor draw improvings * Merge, group, split * Improved colorsmain
parent
247d7f04ec
commit
2cc2e32ff1
@ -0,0 +1,141 @@
|
|||||||
|
import * as SVG from 'svg.js';
|
||||||
|
import consts from './consts';
|
||||||
|
|
||||||
|
import {
|
||||||
|
translateToSVG,
|
||||||
|
} from './shared';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Geometry,
|
||||||
|
} from './canvasModel';
|
||||||
|
|
||||||
|
|
||||||
|
export interface ZoomHandler {
|
||||||
|
zoom(): void;
|
||||||
|
cancel(): void;
|
||||||
|
transform(geometry: Geometry): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ZoomHandlerImpl implements ZoomHandler {
|
||||||
|
private onZoomRegion: (x: number, y: number, width: number, height: number) => void;
|
||||||
|
private bindedOnSelectStart: (event: MouseEvent) => void;
|
||||||
|
private bindedOnSelectUpdate: (event: MouseEvent) => void;
|
||||||
|
private bindedOnSelectStop: (event: MouseEvent) => void;
|
||||||
|
private geometry: Geometry;
|
||||||
|
private canvas: SVG.Container;
|
||||||
|
private selectionRect: SVG.Rect | null;
|
||||||
|
private startSelectionPoint: {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
private onSelectStart(event: MouseEvent): void {
|
||||||
|
if (!this.selectionRect && event.which === 1) {
|
||||||
|
const point = translateToSVG(
|
||||||
|
(this.canvas.node as any as SVGSVGElement),
|
||||||
|
[event.clientX, event.clientY],
|
||||||
|
);
|
||||||
|
this.startSelectionPoint = {
|
||||||
|
x: point[0],
|
||||||
|
y: point[1],
|
||||||
|
};
|
||||||
|
|
||||||
|
this.selectionRect = this.canvas.rect().addClass('cvat_canvas_zoom_selection');
|
||||||
|
this.selectionRect.attr({
|
||||||
|
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
|
||||||
|
...this.startSelectionPoint,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getSelectionBox(event: MouseEvent): {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
} {
|
||||||
|
const point = translateToSVG(
|
||||||
|
(this.canvas.node as any as SVGSVGElement),
|
||||||
|
[event.clientX, event.clientY],
|
||||||
|
);
|
||||||
|
const stopSelectionPoint = {
|
||||||
|
x: point[0],
|
||||||
|
y: point[1],
|
||||||
|
};
|
||||||
|
|
||||||
|
const xtl = Math.min(this.startSelectionPoint.x, stopSelectionPoint.x);
|
||||||
|
const ytl = Math.min(this.startSelectionPoint.y, stopSelectionPoint.y);
|
||||||
|
const xbr = Math.max(this.startSelectionPoint.x, stopSelectionPoint.x);
|
||||||
|
const ybr = Math.max(this.startSelectionPoint.y, stopSelectionPoint.y);
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: xtl,
|
||||||
|
y: ytl,
|
||||||
|
width: xbr - xtl,
|
||||||
|
height: ybr - ytl,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSelectUpdate(event: MouseEvent): void {
|
||||||
|
if (this.selectionRect) {
|
||||||
|
this.selectionRect.attr({
|
||||||
|
...this.getSelectionBox(event),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSelectStop(event: MouseEvent): void {
|
||||||
|
if (this.selectionRect) {
|
||||||
|
const box = this.getSelectionBox(event);
|
||||||
|
this.selectionRect.remove();
|
||||||
|
this.selectionRect = null;
|
||||||
|
this.startSelectionPoint = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
};
|
||||||
|
const threshold = 5;
|
||||||
|
if (box.width > threshold && box.height > threshold) {
|
||||||
|
this.onZoomRegion(box.x, box.y, box.width, box.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
onZoomRegion: (x: number, y: number, width: number, height: number) => void,
|
||||||
|
canvas: SVG.Container,
|
||||||
|
geometry: Geometry,
|
||||||
|
) {
|
||||||
|
this.onZoomRegion = onZoomRegion;
|
||||||
|
this.canvas = canvas;
|
||||||
|
this.geometry = geometry;
|
||||||
|
this.selectionRect = null;
|
||||||
|
this.startSelectionPoint = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
};
|
||||||
|
this.bindedOnSelectStart = this.onSelectStart.bind(this);
|
||||||
|
this.bindedOnSelectUpdate = this.onSelectUpdate.bind(this);
|
||||||
|
this.bindedOnSelectStop = this.onSelectStop.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public zoom(): void {
|
||||||
|
this.canvas.node.addEventListener('mousedown', this.bindedOnSelectStart);
|
||||||
|
this.canvas.node.addEventListener('mousemove', this.bindedOnSelectUpdate);
|
||||||
|
this.canvas.node.addEventListener('mouseup', this.bindedOnSelectStop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public cancel(): void {
|
||||||
|
this.canvas.node.removeEventListener('mousedown', this.bindedOnSelectStart);
|
||||||
|
this.canvas.node.removeEventListener('mousemove', this.bindedOnSelectUpdate);
|
||||||
|
this.canvas.node.removeEventListener('mouseup ', this.bindedOnSelectStop);
|
||||||
|
}
|
||||||
|
|
||||||
|
public transform(geometry: Geometry): void {
|
||||||
|
this.geometry = geometry;
|
||||||
|
if (this.selectionRect) {
|
||||||
|
this.selectionRect.style({
|
||||||
|
'stroke-width': consts.BASE_STROKE_WIDTH / geometry.scale,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,127 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Icon,
|
|
||||||
Layout,
|
|
||||||
Tooltip,
|
|
||||||
Popover,
|
|
||||||
} from 'antd';
|
|
||||||
|
|
||||||
import {
|
|
||||||
CursorIcon,
|
|
||||||
MoveIcon,
|
|
||||||
RotateIcon,
|
|
||||||
FitIcon,
|
|
||||||
ZoomIcon,
|
|
||||||
RectangleIcon,
|
|
||||||
PolygonIcon,
|
|
||||||
PointIcon,
|
|
||||||
PolylineIcon,
|
|
||||||
TagIcon,
|
|
||||||
MergeIcon,
|
|
||||||
GroupIcon,
|
|
||||||
SplitIcon,
|
|
||||||
} from '../../../icons';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Canvas,
|
|
||||||
Rotation,
|
|
||||||
} from '../../../canvas';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
canvasInstance: Canvas;
|
|
||||||
rotateAll: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ControlsSideBarComponent(props: Props): JSX.Element {
|
|
||||||
const {
|
|
||||||
rotateAll,
|
|
||||||
canvasInstance,
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Layout.Sider
|
|
||||||
className='cvat-annotation-page-controls-sidebar'
|
|
||||||
theme='light'
|
|
||||||
width={44}
|
|
||||||
>
|
|
||||||
<Tooltip overlay='Cursor' placement='right'>
|
|
||||||
<Icon component={CursorIcon} />
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip overlay='Move the image' placement='right'>
|
|
||||||
<Icon component={MoveIcon} />
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Popover
|
|
||||||
overlayClassName='cvat-annotation-page-controls-rotate'
|
|
||||||
placement='right'
|
|
||||||
content={(
|
|
||||||
<>
|
|
||||||
<Icon
|
|
||||||
className='cvat-annotation-page-controls-rotate-left'
|
|
||||||
onClick={(): void => canvasInstance
|
|
||||||
.rotate(Rotation.ANTICLOCKWISE90, rotateAll)}
|
|
||||||
component={RotateIcon}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
className='cvat-annotation-page-controls-rotate-right'
|
|
||||||
onClick={(): void => canvasInstance
|
|
||||||
.rotate(Rotation.CLOCKWISE90, rotateAll)}
|
|
||||||
component={RotateIcon}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
trigger='hover'
|
|
||||||
>
|
|
||||||
<Tooltip overlay='Rotate the image' placement='topRight'>
|
|
||||||
<Icon component={RotateIcon} />
|
|
||||||
</Tooltip>
|
|
||||||
</Popover>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<Tooltip overlay='Fit the image' placement='right'>
|
|
||||||
<Icon component={FitIcon} />
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip overlay='Zoom the image' placement='right'>
|
|
||||||
<Icon component={ZoomIcon} />
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<Tooltip overlay='Draw a rectangle' placement='right'>
|
|
||||||
<Icon component={RectangleIcon} />
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip overlay='Draw a polygon' placement='right'>
|
|
||||||
<Icon component={PolygonIcon} />
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip overlay='Draw a polyline' placement='right'>
|
|
||||||
<Icon component={PolylineIcon} />
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip overlay='Draw points' placement='right'>
|
|
||||||
<Icon component={PointIcon} />
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip overlay='Setup a tag' placement='right'>
|
|
||||||
<Icon component={TagIcon} />
|
|
||||||
</Tooltip>
|
|
||||||
<hr />
|
|
||||||
<Tooltip overlay='Merge shapes/tracks' placement='right'>
|
|
||||||
<Icon component={MergeIcon} />
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip overlay='Group shapes/tracks' placement='right'>
|
|
||||||
<Icon component={GroupIcon} />
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip overlay='Split a track' placement='right'>
|
|
||||||
<Icon component={SplitIcon} />
|
|
||||||
</Tooltip>
|
|
||||||
</Layout.Sider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Icon,
|
||||||
|
Layout,
|
||||||
|
Tooltip,
|
||||||
|
} from 'antd';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ActiveControl,
|
||||||
|
} from 'reducers/interfaces';
|
||||||
|
|
||||||
|
import {
|
||||||
|
TagIcon,
|
||||||
|
} from 'icons';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Canvas,
|
||||||
|
} from 'cvat-canvas';
|
||||||
|
|
||||||
|
import CursorControl from './cursor-control';
|
||||||
|
import MoveControl from './move-control';
|
||||||
|
import RotateControl from './rotate-control';
|
||||||
|
import FitControl from './fit-control';
|
||||||
|
import ResizeControl from './resize-control';
|
||||||
|
import DrawRectangleControl from './draw-rectangle-control';
|
||||||
|
import DrawPolygonControl from './draw-polygon-control';
|
||||||
|
import DrawPolylineControl from './draw-polyline-control';
|
||||||
|
import DrawPointsControl from './draw-points-control';
|
||||||
|
import MergeControl from './merge-control';
|
||||||
|
import GroupControl from './group-control';
|
||||||
|
import SplitControl from './split-control';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
rotateAll: boolean;
|
||||||
|
activeControl: ActiveControl;
|
||||||
|
|
||||||
|
onMergeStart(): void;
|
||||||
|
onGroupStart(): void;
|
||||||
|
onSplitStart(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ControlsSideBarComponent(props: Props): JSX.Element {
|
||||||
|
return (
|
||||||
|
<Layout.Sider
|
||||||
|
className='cvat-annotation-page-controls-sidebar'
|
||||||
|
theme='light'
|
||||||
|
width={44}
|
||||||
|
>
|
||||||
|
<CursorControl {...props} />
|
||||||
|
<MoveControl {...props} />
|
||||||
|
<RotateControl {...props} />
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<FitControl {...props} />
|
||||||
|
<ResizeControl {...props} />
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<DrawRectangleControl {...props} />
|
||||||
|
<DrawPolygonControl {...props} />
|
||||||
|
<DrawPolylineControl {...props} />
|
||||||
|
<DrawPointsControl {...props} />
|
||||||
|
|
||||||
|
<Tooltip overlay='Setup a tag' placement='right'>
|
||||||
|
<Icon component={TagIcon} />
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<MergeControl {...props} />
|
||||||
|
<GroupControl {...props} />
|
||||||
|
<SplitControl {...props} />
|
||||||
|
</Layout.Sider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Icon,
|
||||||
|
Tooltip,
|
||||||
|
} from 'antd';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CursorIcon,
|
||||||
|
} from 'icons';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ActiveControl,
|
||||||
|
} from 'reducers/interfaces';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Canvas,
|
||||||
|
} from 'cvat-canvas';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
activeControl: ActiveControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CursorControl(props: Props): JSX.Element {
|
||||||
|
const {
|
||||||
|
canvasInstance,
|
||||||
|
activeControl,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip overlay='Cursor' placement='right'>
|
||||||
|
<Icon
|
||||||
|
component={CursorIcon}
|
||||||
|
className={activeControl === ActiveControl.CURSOR
|
||||||
|
? 'cvat-annotation-page-active-control' : ''
|
||||||
|
}
|
||||||
|
onClick={(): void => {
|
||||||
|
if (activeControl !== ActiveControl.CURSOR) {
|
||||||
|
canvasInstance.cancel();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
Icon,
|
||||||
|
} from 'antd';
|
||||||
|
|
||||||
|
import { Canvas } from 'cvat-canvas';
|
||||||
|
import { PointIcon } from 'icons';
|
||||||
|
import {
|
||||||
|
ShapeType,
|
||||||
|
ActiveControl,
|
||||||
|
} from 'reducers/interfaces';
|
||||||
|
|
||||||
|
import DrawShapePopoverContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
activeControl: ActiveControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DrawRectangleControl(props: Props): JSX.Element {
|
||||||
|
const {
|
||||||
|
canvasInstance,
|
||||||
|
activeControl,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const dynamcPopoverPros = activeControl === ActiveControl.DRAW_POINTS
|
||||||
|
? {
|
||||||
|
overlayStyle: {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
} : {};
|
||||||
|
|
||||||
|
const dynamicIconProps = activeControl === ActiveControl.DRAW_POINTS
|
||||||
|
? {
|
||||||
|
className: 'cvat-annotation-page-active-control',
|
||||||
|
onClick: (): void => {
|
||||||
|
canvasInstance.draw({ enabled: false });
|
||||||
|
},
|
||||||
|
} : {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
{...dynamcPopoverPros}
|
||||||
|
overlayClassName='cvat-draw-shape-popover'
|
||||||
|
placement='right'
|
||||||
|
content={(
|
||||||
|
<DrawShapePopoverContainer shapeType={ShapeType.POINTS} />
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
{...dynamicIconProps}
|
||||||
|
component={PointIcon}
|
||||||
|
/>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
Icon,
|
||||||
|
} from 'antd';
|
||||||
|
|
||||||
|
import { Canvas } from 'cvat-canvas';
|
||||||
|
import { PolygonIcon } from 'icons';
|
||||||
|
import {
|
||||||
|
ShapeType,
|
||||||
|
ActiveControl,
|
||||||
|
} from 'reducers/interfaces';
|
||||||
|
|
||||||
|
import DrawShapePopoverContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
activeControl: ActiveControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DrawRectangleControl(props: Props): JSX.Element {
|
||||||
|
const {
|
||||||
|
canvasInstance,
|
||||||
|
activeControl,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const dynamcPopoverPros = activeControl === ActiveControl.DRAW_POLYGON
|
||||||
|
? {
|
||||||
|
overlayStyle: {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
} : {};
|
||||||
|
|
||||||
|
const dynamicIconProps = activeControl === ActiveControl.DRAW_POLYGON
|
||||||
|
? {
|
||||||
|
className: 'cvat-annotation-page-active-control',
|
||||||
|
onClick: (): void => {
|
||||||
|
canvasInstance.draw({ enabled: false });
|
||||||
|
},
|
||||||
|
} : {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
{...dynamcPopoverPros}
|
||||||
|
overlayClassName='cvat-draw-shape-popover'
|
||||||
|
placement='right'
|
||||||
|
content={(
|
||||||
|
<DrawShapePopoverContainer shapeType={ShapeType.POLYGON} />
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
{...dynamicIconProps}
|
||||||
|
component={PolygonIcon}
|
||||||
|
/>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
Icon,
|
||||||
|
} from 'antd';
|
||||||
|
|
||||||
|
import { Canvas } from 'cvat-canvas';
|
||||||
|
import { PolylineIcon } from 'icons';
|
||||||
|
import {
|
||||||
|
ShapeType,
|
||||||
|
ActiveControl,
|
||||||
|
} from 'reducers/interfaces';
|
||||||
|
|
||||||
|
import DrawShapePopoverContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
activeControl: ActiveControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DrawRectangleControl(props: Props): JSX.Element {
|
||||||
|
const {
|
||||||
|
canvasInstance,
|
||||||
|
activeControl,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const dynamcPopoverPros = activeControl === ActiveControl.DRAW_POLYLINE
|
||||||
|
? {
|
||||||
|
overlayStyle: {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
} : {};
|
||||||
|
|
||||||
|
const dynamicIconProps = activeControl === ActiveControl.DRAW_POLYLINE
|
||||||
|
? {
|
||||||
|
className: 'cvat-annotation-page-active-control',
|
||||||
|
onClick: (): void => {
|
||||||
|
canvasInstance.draw({ enabled: false });
|
||||||
|
},
|
||||||
|
} : {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
{...dynamcPopoverPros}
|
||||||
|
overlayClassName='cvat-draw-shape-popover'
|
||||||
|
placement='right'
|
||||||
|
content={(
|
||||||
|
<DrawShapePopoverContainer shapeType={ShapeType.POLYLINE} />
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
{...dynamicIconProps}
|
||||||
|
component={PolylineIcon}
|
||||||
|
/>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
Icon,
|
||||||
|
} from 'antd';
|
||||||
|
|
||||||
|
import { Canvas } from 'cvat-canvas';
|
||||||
|
import { RectangleIcon } from 'icons';
|
||||||
|
import {
|
||||||
|
ShapeType,
|
||||||
|
ActiveControl,
|
||||||
|
} from 'reducers/interfaces';
|
||||||
|
|
||||||
|
import DrawShapePopoverContainer from 'containers/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
activeControl: ActiveControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DrawRectangleControl(props: Props): JSX.Element {
|
||||||
|
const {
|
||||||
|
canvasInstance,
|
||||||
|
activeControl,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const dynamcPopoverPros = activeControl === ActiveControl.DRAW_RECTANGLE
|
||||||
|
? {
|
||||||
|
overlayStyle: {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
} : {};
|
||||||
|
|
||||||
|
const dynamicIconProps = activeControl === ActiveControl.DRAW_RECTANGLE
|
||||||
|
? {
|
||||||
|
className: 'cvat-annotation-page-active-control',
|
||||||
|
onClick: (): void => {
|
||||||
|
canvasInstance.draw({ enabled: false });
|
||||||
|
},
|
||||||
|
} : {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
{...dynamcPopoverPros}
|
||||||
|
overlayClassName='cvat-draw-shape-popover'
|
||||||
|
placement='right'
|
||||||
|
content={(
|
||||||
|
<DrawShapePopoverContainer shapeType={ShapeType.RECTANGLE} />
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
{...dynamicIconProps}
|
||||||
|
component={RectangleIcon}
|
||||||
|
/>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,186 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Row,
|
||||||
|
Col,
|
||||||
|
Select,
|
||||||
|
Button,
|
||||||
|
InputNumber,
|
||||||
|
} from 'antd';
|
||||||
|
|
||||||
|
import Text from 'antd/lib/typography/Text';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ShapeType,
|
||||||
|
ObjectType,
|
||||||
|
StringObject,
|
||||||
|
} from 'reducers/interfaces';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Canvas,
|
||||||
|
} from 'cvat-canvas';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
shapeType: ShapeType;
|
||||||
|
labels: StringObject;
|
||||||
|
|
||||||
|
onDrawStart(
|
||||||
|
shapeType: ShapeType,
|
||||||
|
labelID: number,
|
||||||
|
objectType: ObjectType,
|
||||||
|
points?: number,
|
||||||
|
): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
numberOfPoints?: number;
|
||||||
|
selectedLabeID: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function defineMinimumPoints(shapeType: ShapeType): number {
|
||||||
|
if (shapeType === ShapeType.POLYGON) {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
if (shapeType === ShapeType.POLYLINE) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if (shapeType === ShapeType.POINTS) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class DrawShapePopoverComponent extends React.PureComponent<Props, State> {
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
const defaultLabelID = +Object.keys(props.labels)[0];
|
||||||
|
this.state = {
|
||||||
|
selectedLabeID: defaultLabelID,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private onChangePoints = (value: number | undefined): void => {
|
||||||
|
this.setState({
|
||||||
|
numberOfPoints: value,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private onChangeLabel = (value: string): void => {
|
||||||
|
this.setState({
|
||||||
|
selectedLabeID: +value,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private onDrawTrackStart = (): void => {
|
||||||
|
this.onDrawStart(ObjectType.TRACK);
|
||||||
|
};
|
||||||
|
|
||||||
|
private onDrawShapeStart = (): void => {
|
||||||
|
this.onDrawStart(ObjectType.SHAPE);
|
||||||
|
};
|
||||||
|
|
||||||
|
private onDrawStart = (objectType: ObjectType): void => {
|
||||||
|
const {
|
||||||
|
numberOfPoints,
|
||||||
|
selectedLabeID,
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
const {
|
||||||
|
shapeType,
|
||||||
|
onDrawStart,
|
||||||
|
canvasInstance,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
canvasInstance.cancel();
|
||||||
|
canvasInstance.draw({
|
||||||
|
enabled: true,
|
||||||
|
numberOfPoints,
|
||||||
|
shapeType,
|
||||||
|
crosshair: shapeType === ShapeType.RECTANGLE,
|
||||||
|
});
|
||||||
|
|
||||||
|
onDrawStart(shapeType, selectedLabeID,
|
||||||
|
objectType, numberOfPoints);
|
||||||
|
};
|
||||||
|
|
||||||
|
public render(): JSX.Element {
|
||||||
|
const {
|
||||||
|
selectedLabeID,
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
const {
|
||||||
|
shapeType,
|
||||||
|
labels,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const minimumPoints = defineMinimumPoints(shapeType);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='cvat-draw-shape-popover-content'>
|
||||||
|
<Row type='flex' justify='start'>
|
||||||
|
<Col>
|
||||||
|
<Text className='cvat-text-color' strong>{`Draw new ${shapeType}`}</Text>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row type='flex' justify='start'>
|
||||||
|
<Col>
|
||||||
|
<Text className='cvat-text-color'>Label</Text>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row type='flex' justify='center'>
|
||||||
|
<Col span={24}>
|
||||||
|
<Select
|
||||||
|
value={labels[selectedLabeID]}
|
||||||
|
onChange={this.onChangeLabel}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
Object.keys(labels).map((key: string) => (
|
||||||
|
<Select.Option
|
||||||
|
key={key}
|
||||||
|
value={key}
|
||||||
|
>
|
||||||
|
{labels[+key]}
|
||||||
|
</Select.Option>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
{
|
||||||
|
shapeType !== ShapeType.RECTANGLE && (
|
||||||
|
<Row type='flex' justify='space-around' align='middle'>
|
||||||
|
<Col span={14}>
|
||||||
|
<Text className='cvat-text-color'> Number of points: </Text>
|
||||||
|
</Col>
|
||||||
|
<Col span={10}>
|
||||||
|
<InputNumber
|
||||||
|
onChange={this.onChangePoints}
|
||||||
|
className='cvat-draw-shape-popover-points-selector'
|
||||||
|
min={minimumPoints}
|
||||||
|
step={1}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<Row type='flex' justify='space-around'>
|
||||||
|
<Col span={12}>
|
||||||
|
<Button
|
||||||
|
onClick={this.onDrawShapeStart}
|
||||||
|
>
|
||||||
|
Shape
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<Button
|
||||||
|
onClick={this.onDrawTrackStart}
|
||||||
|
>
|
||||||
|
Track
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Icon,
|
||||||
|
Tooltip,
|
||||||
|
} from 'antd';
|
||||||
|
|
||||||
|
import {
|
||||||
|
FitIcon,
|
||||||
|
} from 'icons';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Canvas,
|
||||||
|
} from 'cvat-canvas';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function FitControl(props: Props): JSX.Element {
|
||||||
|
const {
|
||||||
|
canvasInstance,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip overlay='Fit the image' placement='right'>
|
||||||
|
<Icon component={FitIcon} onClick={(): void => canvasInstance.fit()} />
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
Icon,
|
||||||
|
} from 'antd';
|
||||||
|
|
||||||
|
import {
|
||||||
|
GroupIcon,
|
||||||
|
} from 'icons';
|
||||||
|
|
||||||
|
import { Canvas } from 'cvat-canvas';
|
||||||
|
import { ActiveControl } from 'reducers/interfaces';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
activeControl: ActiveControl;
|
||||||
|
|
||||||
|
onGroupStart(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function GroupControl(props: Props): JSX.Element {
|
||||||
|
const {
|
||||||
|
activeControl,
|
||||||
|
canvasInstance,
|
||||||
|
onGroupStart,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const dynamicIconProps = activeControl === ActiveControl.GROUP
|
||||||
|
? {
|
||||||
|
className: 'cvat-annotation-page-active-control',
|
||||||
|
onClick: (): void => {
|
||||||
|
canvasInstance.group({ enabled: false });
|
||||||
|
},
|
||||||
|
} : {
|
||||||
|
onClick: (): void => {
|
||||||
|
canvasInstance.cancel();
|
||||||
|
canvasInstance.group({ enabled: true });
|
||||||
|
onGroupStart();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip overlay='Group shapes/tracks' placement='right'>
|
||||||
|
<Icon {...dynamicIconProps} component={GroupIcon} />
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
Icon,
|
||||||
|
} from 'antd';
|
||||||
|
|
||||||
|
import {
|
||||||
|
MergeIcon,
|
||||||
|
} from 'icons';
|
||||||
|
|
||||||
|
import { Canvas } from 'cvat-canvas';
|
||||||
|
import { ActiveControl } from 'reducers/interfaces';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
activeControl: ActiveControl;
|
||||||
|
|
||||||
|
onMergeStart(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MergeControl(props: Props): JSX.Element {
|
||||||
|
const {
|
||||||
|
activeControl,
|
||||||
|
canvasInstance,
|
||||||
|
onMergeStart,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const dynamicIconProps = activeControl === ActiveControl.MERGE
|
||||||
|
? {
|
||||||
|
className: 'cvat-annotation-page-active-control',
|
||||||
|
onClick: (): void => {
|
||||||
|
canvasInstance.merge({ enabled: false });
|
||||||
|
},
|
||||||
|
} : {
|
||||||
|
onClick: (): void => {
|
||||||
|
canvasInstance.cancel();
|
||||||
|
canvasInstance.merge({ enabled: true });
|
||||||
|
onMergeStart();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip overlay='Merge shapes/tracks' placement='right'>
|
||||||
|
<Icon {...dynamicIconProps} component={MergeIcon} />
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Icon,
|
||||||
|
Tooltip,
|
||||||
|
} from 'antd';
|
||||||
|
|
||||||
|
import {
|
||||||
|
MoveIcon,
|
||||||
|
} from 'icons';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ActiveControl,
|
||||||
|
} from 'reducers/interfaces';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Canvas,
|
||||||
|
} from 'cvat-canvas';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
activeControl: ActiveControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MoveControl(props: Props): JSX.Element {
|
||||||
|
const {
|
||||||
|
canvasInstance,
|
||||||
|
activeControl,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip overlay='Move the image' placement='right'>
|
||||||
|
<Icon
|
||||||
|
component={MoveIcon}
|
||||||
|
className={activeControl === ActiveControl.DRAG_CANVAS
|
||||||
|
? 'cvat-annotation-page-active-control' : ''
|
||||||
|
}
|
||||||
|
onClick={(): void => {
|
||||||
|
if (activeControl === ActiveControl.DRAG_CANVAS) {
|
||||||
|
canvasInstance.dragCanvas(false);
|
||||||
|
} else {
|
||||||
|
canvasInstance.cancel();
|
||||||
|
canvasInstance.dragCanvas(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Icon,
|
||||||
|
Tooltip,
|
||||||
|
} from 'antd';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ZoomIcon,
|
||||||
|
} from 'icons';
|
||||||
|
|
||||||
|
import {
|
||||||
|
ActiveControl,
|
||||||
|
} from 'reducers/interfaces';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Canvas,
|
||||||
|
} from 'cvat-canvas';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
activeControl: ActiveControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ResizeControl(props: Props): JSX.Element {
|
||||||
|
const {
|
||||||
|
activeControl,
|
||||||
|
canvasInstance,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip overlay='Select a region of interest' placement='right'>
|
||||||
|
<Icon
|
||||||
|
component={ZoomIcon}
|
||||||
|
className={activeControl === ActiveControl.ZOOM_CANVAS
|
||||||
|
? 'cvat-annotation-page-active-control' : ''
|
||||||
|
}
|
||||||
|
onClick={(): void => {
|
||||||
|
if (activeControl === ActiveControl.ZOOM_CANVAS) {
|
||||||
|
canvasInstance.zoomCanvas(false);
|
||||||
|
} else {
|
||||||
|
canvasInstance.cancel();
|
||||||
|
canvasInstance.zoomCanvas(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Icon,
|
||||||
|
Tooltip,
|
||||||
|
Popover,
|
||||||
|
} from 'antd';
|
||||||
|
|
||||||
|
import {
|
||||||
|
RotateIcon,
|
||||||
|
} from 'icons';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Rotation,
|
||||||
|
Canvas,
|
||||||
|
} from 'cvat-canvas';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
rotateAll: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function RotateControl(props: Props): JSX.Element {
|
||||||
|
const {
|
||||||
|
rotateAll,
|
||||||
|
canvasInstance,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
overlayClassName='cvat-annotation-page-controls-rotate'
|
||||||
|
placement='right'
|
||||||
|
content={(
|
||||||
|
<>
|
||||||
|
<Tooltip overlay='Rotate the image anticlockwise' placement='topRight'>
|
||||||
|
<Icon
|
||||||
|
className='cvat-annotation-page-controls-rotate-left'
|
||||||
|
onClick={(): void => canvasInstance
|
||||||
|
.rotate(Rotation.ANTICLOCKWISE90, rotateAll)}
|
||||||
|
component={RotateIcon}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip overlay='Rotate the image clockwise' placement='topRight'>
|
||||||
|
<Icon
|
||||||
|
className='cvat-annotation-page-controls-rotate-right'
|
||||||
|
onClick={(): void => canvasInstance
|
||||||
|
.rotate(Rotation.CLOCKWISE90, rotateAll)}
|
||||||
|
component={RotateIcon}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
trigger='hover'
|
||||||
|
>
|
||||||
|
<Icon component={RotateIcon} />
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
Icon,
|
||||||
|
} from 'antd';
|
||||||
|
|
||||||
|
import {
|
||||||
|
SplitIcon,
|
||||||
|
} from 'icons';
|
||||||
|
|
||||||
|
import { Canvas } from 'cvat-canvas';
|
||||||
|
import { ActiveControl } from 'reducers/interfaces';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
activeControl: ActiveControl;
|
||||||
|
|
||||||
|
onSplitStart(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SplitControl(props: Props): JSX.Element {
|
||||||
|
const {
|
||||||
|
activeControl,
|
||||||
|
canvasInstance,
|
||||||
|
onSplitStart,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const dynamicIconProps = activeControl === ActiveControl.SPLIT
|
||||||
|
? {
|
||||||
|
className: 'cvat-annotation-page-active-control',
|
||||||
|
onClick: (): void => {
|
||||||
|
canvasInstance.split({ enabled: false });
|
||||||
|
},
|
||||||
|
} : {
|
||||||
|
onClick: (): void => {
|
||||||
|
canvasInstance.cancel();
|
||||||
|
canvasInstance.split({ enabled: true });
|
||||||
|
onSplitStart();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip overlay='Split a track' placement='right'>
|
||||||
|
<Icon {...dynamicIconProps} component={SplitIcon} />
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,35 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import { Canvas } from '../../../canvas';
|
|
||||||
|
|
||||||
import ControlsSideBarComponent from '../../../components/annotation-page/standard-workspace/controls-side-bar';
|
|
||||||
import { CombinedState } from '../../../reducers/interfaces';
|
|
||||||
|
|
||||||
|
|
||||||
interface StateToProps {
|
|
||||||
canvasInstance: Canvas;
|
|
||||||
rotateAll: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapStateToProps(state: CombinedState): StateToProps {
|
|
||||||
const {
|
|
||||||
annotation,
|
|
||||||
settings,
|
|
||||||
} = state;
|
|
||||||
|
|
||||||
return {
|
|
||||||
rotateAll: settings.player.rotateAll,
|
|
||||||
canvasInstance: annotation.canvasInstance,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function StandardWorkspaceContainer(props: StateToProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<ControlsSideBarComponent {...props} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
)(StandardWorkspaceContainer);
|
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { Canvas } from 'cvat-canvas';
|
||||||
|
|
||||||
|
import {
|
||||||
|
mergeObjects,
|
||||||
|
groupObjects,
|
||||||
|
splitTrack,
|
||||||
|
} from 'actions/annotation-actions';
|
||||||
|
import ControlsSideBarComponent from 'components/annotation-page/standard-workspace/controls-side-bar/controls-side-bar';
|
||||||
|
import {
|
||||||
|
ActiveControl,
|
||||||
|
CombinedState,
|
||||||
|
StringObject,
|
||||||
|
} from 'reducers/interfaces';
|
||||||
|
|
||||||
|
interface StateToProps {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
rotateAll: boolean;
|
||||||
|
activeControl: ActiveControl;
|
||||||
|
labels: StringObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DispatchToProps {
|
||||||
|
onMergeStart(): void;
|
||||||
|
onGroupStart(): void;
|
||||||
|
onSplitStart(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: CombinedState): StateToProps {
|
||||||
|
const {
|
||||||
|
annotation,
|
||||||
|
settings,
|
||||||
|
} = state;
|
||||||
|
|
||||||
|
const {
|
||||||
|
canvasInstance,
|
||||||
|
activeControl,
|
||||||
|
} = annotation;
|
||||||
|
|
||||||
|
const labels = annotation.jobInstance.task.labels
|
||||||
|
.reduce((acc: StringObject, label: any): StringObject => {
|
||||||
|
acc[label.id as number] = label.name;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return {
|
||||||
|
rotateAll: settings.player.rotateAll,
|
||||||
|
canvasInstance,
|
||||||
|
activeControl,
|
||||||
|
labels,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchToProps(dispatch: any): DispatchToProps {
|
||||||
|
return {
|
||||||
|
onMergeStart(): void {
|
||||||
|
dispatch(mergeObjects());
|
||||||
|
},
|
||||||
|
onGroupStart(): void {
|
||||||
|
dispatch(groupObjects());
|
||||||
|
},
|
||||||
|
onSplitStart(): void {
|
||||||
|
dispatch(splitTrack());
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function StandardWorkspaceContainer(props: StateToProps & DispatchToProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<ControlsSideBarComponent {...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
dispatchToProps,
|
||||||
|
)(StandardWorkspaceContainer);
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CombinedState,
|
||||||
|
ShapeType,
|
||||||
|
ObjectType,
|
||||||
|
StringObject,
|
||||||
|
} from 'reducers/interfaces';
|
||||||
|
|
||||||
|
import {
|
||||||
|
drawShape,
|
||||||
|
} from 'actions/annotation-actions';
|
||||||
|
import { Canvas } from 'cvat-canvas';
|
||||||
|
import DrawShapePopoverComponent from 'components/annotation-page/standard-workspace/controls-side-bar/draw-shape-popover';
|
||||||
|
|
||||||
|
interface OwnProps {
|
||||||
|
shapeType: ShapeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DispatchToProps {
|
||||||
|
onDrawStart(
|
||||||
|
shapeType: ShapeType,
|
||||||
|
labelID: number,
|
||||||
|
objectType: ObjectType,
|
||||||
|
points?: number,
|
||||||
|
): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StateToProps {
|
||||||
|
canvasInstance: Canvas;
|
||||||
|
shapeType: ShapeType;
|
||||||
|
labels: StringObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps(dispatch: any): DispatchToProps {
|
||||||
|
return {
|
||||||
|
onDrawStart(
|
||||||
|
shapeType: ShapeType,
|
||||||
|
labelID: number,
|
||||||
|
objectType: ObjectType,
|
||||||
|
points?: number,
|
||||||
|
): void {
|
||||||
|
dispatch(drawShape(shapeType, labelID, objectType, points));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
|
||||||
|
const {
|
||||||
|
annotation,
|
||||||
|
} = state;
|
||||||
|
|
||||||
|
const {
|
||||||
|
canvasInstance,
|
||||||
|
} = annotation;
|
||||||
|
|
||||||
|
const labels = annotation.jobInstance.task.labels
|
||||||
|
.reduce((acc: StringObject, label: any): StringObject => {
|
||||||
|
acc[label.id as number] = label.name;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...own,
|
||||||
|
canvasInstance,
|
||||||
|
labels,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function DrawShapePopoverContainer(props: DispatchToProps & StateToProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<DrawShapePopoverComponent {...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
)(DrawShapePopoverContainer);
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue