Skip to content

Commit

Permalink
Add extreme clicking method in cvat-canvas and cvat-ui (#1127)
Browse files Browse the repository at this point in the history
* Add extreme clicking method in cvat-canvas and cvat-ui

* Fix bugs and issues, update readme

* Fix error after rebasing develop
  • Loading branch information
Jooong authored Feb 12, 2020
1 parent 20ed049 commit d9fe712
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 8 deletions.
1 change: 1 addition & 0 deletions cvat-canvas/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Canvas itself handles:
interface DrawData {
enabled: boolean;
shapeType?: string;
rectDrawingMethod?: string;
numberOfPoints?: number;
initialState?: any;
crosshair?: boolean;
Expand Down
1 change: 1 addition & 0 deletions cvat-canvas/src/typescript/canvasModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface ActiveElement {
export interface DrawData {
enabled: boolean;
shapeType?: string;
rectDrawingMethod?: string;
numberOfPoints?: number;
initialState?: any;
crosshair?: boolean;
Expand Down
56 changes: 50 additions & 6 deletions cvat-canvas/src/typescript/drawHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ export class DrawHandlerImpl implements DrawHandler {
if (!this.drawData.initialState) {
const { drawInstance } = this;
this.drawInstance = null;
if (this.drawData.shapeType === 'rectangle') {
if (this.drawData.shapeType === 'rectangle'
&& this.drawData.rectDrawingMethod !== 'by_four_points') {
drawInstance.draw('cancel');
} else {
drawInstance.draw('done');
Expand Down Expand Up @@ -200,6 +201,45 @@ export class DrawHandlerImpl implements DrawHandler {
});
}

private drawBoxBy4Points(): void {
let numberOfPoints = 0;
this.drawInstance = (this.canvas as any).polygon()
.addClass('cvat_canvas_shape_drawing').attr({
'stroke-width': 0,
opacity: 0,
}).on('drawstart', () => {
// init numberOfPoints as one on drawstart
numberOfPoints = 1;
}).on('drawpoint', (e: CustomEvent) => {
// increase numberOfPoints by one on drawpoint
numberOfPoints += 1;

// finish if numberOfPoints are exactly four
if (numberOfPoints === 4) {
const bbox = (e.target as SVGPolylineElement).getBBox();
const [xtl, ytl, xbr, ybr] = this.getFinalRectCoordinates(bbox);

if ((xbr - xtl) * (ybr - ytl) >= consts.AREA_THRESHOLD) {
this.onDrawDone({
shapeType: this.drawData.shapeType,
points: [xtl, ytl, xbr, ybr],
});
} else {
this.onDrawDone(null);
}
}
}).on('undopoint', () => {
if (numberOfPoints > 0) {
numberOfPoints -= 1;
}
}).off('drawdone').on('drawdone', () => {
// close drawing mode without drawing rect
this.onDrawDone(null);
});

this.drawPolyshape();
}

private drawPolyshape(): void {
this.drawInstance.attr({
z_order: Number.MAX_SAFE_INTEGER,
Expand Down Expand Up @@ -536,21 +576,25 @@ export class DrawHandlerImpl implements DrawHandler {
this.pastePoints(stringifiedPoints);
}
}

this.setupPasteEvents();
} else {
if (this.drawData.shapeType === 'rectangle') {
this.drawBox();
// Draw instance was initialized after drawBox();
this.shapeSizeElement = displayShapeSize(this.canvas, this.text);
if (this.drawData.rectDrawingMethod === 'by_four_points') {
// draw box by extreme clicking
this.drawBoxBy4Points();
} else {
// default box drawing
this.drawBox();
// Draw instance was initialized after drawBox();
this.shapeSizeElement = displayShapeSize(this.canvas, this.text);
}
} else if (this.drawData.shapeType === 'polygon') {
this.drawPolygon();
} else if (this.drawData.shapeType === 'polyline') {
this.drawPolyline();
} else if (this.drawData.shapeType === 'points') {
this.drawPoints();
}

this.setupDrawEvents();
}
}
Expand Down
2 changes: 2 additions & 0 deletions cvat-ui/src/actions/annotation-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,7 @@ export function drawShape(
labelID: number,
objectType: ObjectType,
points?: number,
rectDrawingMethod?: string,
): AnyAction {
let activeControl = ActiveControl.DRAW_RECTANGLE;
if (shapeType === ShapeType.POLYGON) {
Expand All @@ -601,6 +602,7 @@ export function drawShape(
objectType,
points,
activeControl,
rectDrawingMethod,
},
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,27 @@ import {
Select,
Button,
InputNumber,
Radio,
} from 'antd';

import { RadioChangeEvent } from 'antd/lib/radio';
import Text from 'antd/lib/typography/Text';

import {
ShapeType,
RectDrawingMethod,
} from 'reducers/interfaces';

interface Props {
shapeType: ShapeType;
rectDrawingMethod: RectDrawingMethod;
labels: any[];
minimumPoints: number;
numberOfPoints?: number;
selectedLabeID: number;
onChangeLabel(value: string): void;
onChangePoints(value: number | undefined): void;
onChangeRectDrawingMethod(event: RadioChangeEvent): void;
onDrawTrack(): void;
onDrawShape(): void;
}
Expand All @@ -37,6 +42,7 @@ function DrawShapePopoverComponent(props: Props): JSX.Element {
onDrawShape,
onChangeLabel,
onChangePoints,
onChangeRectDrawingMethod,
} = props;

return (
Expand Down Expand Up @@ -71,7 +77,37 @@ function DrawShapePopoverComponent(props: Props): JSX.Element {
</Col>
</Row>
{
shapeType !== ShapeType.RECTANGLE && (
shapeType === ShapeType.RECTANGLE ? (
<>
<Row>
<Col>
<Text className='cvat-text-color'> Drawing method </Text>
</Col>
</Row>
<Row type='flex' justify='space-around'>
<Col>
<Radio.Group
style={{ display: 'flex' }}
defaultValue={RectDrawingMethod.BY_TWO_POINTS}
onChange={onChangeRectDrawingMethod}
>
<Radio
value={RectDrawingMethod.BY_TWO_POINTS}
style={{ width: 'auto' }}
>
By 2 Points
</Radio>
<Radio
value={RectDrawingMethod.BY_FOUR_POINTS}
style={{ width: 'auto' }}
>
By 4 Points
</Radio>
</Radio.Group>
</Col>
</Row>
</>
) : (
<Row type='flex' justify='space-around' align='middle'>
<Col span={14}>
<Text className='cvat-text-color'> Number of points: </Text>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React from 'react';
import { connect } from 'react-redux';
import { RadioChangeEvent } from 'antd/lib/radio';

import {
CombinedState,
ShapeType,
ObjectType,
RectDrawingMethod,
} from 'reducers/interfaces';

import {
Expand All @@ -23,6 +25,7 @@ interface DispatchToProps {
labelID: number,
objectType: ObjectType,
points?: number,
rectDrawingMethod?: string,
): void;
}

Expand All @@ -39,8 +42,9 @@ function mapDispatchToProps(dispatch: any): DispatchToProps {
labelID: number,
objectType: ObjectType,
points?: number,
rectDrawingMethod?: string,
): void {
dispatch(drawShape(shapeType, labelID, objectType, points));
dispatch(drawShape(shapeType, labelID, objectType, points, rectDrawingMethod));
},
};
}
Expand All @@ -67,6 +71,7 @@ function mapStateToProps(state: CombinedState, own: OwnProps): StateToProps {
type Props = StateToProps & DispatchToProps;

interface State {
rectDrawingMethod?: string;
numberOfPoints?: number;
selectedLabelID: number;
}
Expand All @@ -77,6 +82,7 @@ class DrawShapePopoverContainer extends React.PureComponent<Props, State> {
super(props);

const defaultLabelID = props.labels[0].id;
const defaultRectDrawingMethod = RectDrawingMethod.BY_TWO_POINTS;
this.state = {
selectedLabelID: defaultLabelID,
};
Expand All @@ -91,6 +97,9 @@ class DrawShapePopoverContainer extends React.PureComponent<Props, State> {
if (shapeType === ShapeType.POINTS) {
this.minimumPoints = 1;
}
if (shapeType === ShapeType.RECTANGLE) {
this.state.rectDrawingMethod = defaultRectDrawingMethod;
}
}

private onDraw(objectType: ObjectType): void {
Expand All @@ -101,13 +110,15 @@ class DrawShapePopoverContainer extends React.PureComponent<Props, State> {
} = this.props;

const {
rectDrawingMethod,
numberOfPoints,
selectedLabelID,
} = this.state;

canvasInstance.cancel();
canvasInstance.draw({
enabled: true,
rectDrawingMethod,
numberOfPoints,
shapeType,
crosshair: shapeType === ShapeType.RECTANGLE,
Expand All @@ -117,6 +128,12 @@ class DrawShapePopoverContainer extends React.PureComponent<Props, State> {
objectType, numberOfPoints);
}

private onChangeRectDrawingMethod = (event: RadioChangeEvent): void => {
this.setState({
rectDrawingMethod: event.target.value,
});
};

private onDrawShape = (): void => {
this.onDraw(ObjectType.SHAPE);
};
Expand Down Expand Up @@ -163,6 +180,7 @@ class DrawShapePopoverContainer extends React.PureComponent<Props, State> {
numberOfPoints={numberOfPoints}
onChangeLabel={this.onChangeLabel}
onChangePoints={this.onChangePoints}
onChangeRectDrawingMethod={this.onChangeRectDrawingMethod}
onDrawTrack={this.onDrawTrack}
onDrawShape={this.onDrawShape}
/>
Expand Down
2 changes: 2 additions & 0 deletions cvat-ui/src/reducers/annotation-reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ export default (state = defaultState, action: AnyAction): AnnotationState => {
objectType,
points,
activeControl,
rectDrawingMethod,
} = action.payload;

return {
Expand All @@ -355,6 +356,7 @@ export default (state = defaultState, action: AnyAction): AnnotationState => {
activeNumOfPoints: points,
activeObjectType: objectType,
activeShapeType: shapeType,
activeRectDrawingMethod: rectDrawingMethod,
},
};
}
Expand Down
6 changes: 6 additions & 0 deletions cvat-ui/src/reducers/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,11 @@ export enum ActiveControl {
EDIT = 'edit',
}

export enum RectDrawingMethod {
BY_TWO_POINTS = 'by_two_points',
BY_FOUR_POINTS = 'by_four_points'
}

export enum ShapeType {
RECTANGLE = 'rectangle',
POLYGON = 'polygon',
Expand Down Expand Up @@ -297,6 +302,7 @@ export interface AnnotationState {
};
drawing: {
activeShapeType: ShapeType;
activeRectDrawingMethod?: RectDrawingMethod;
activeNumOfPoints?: number;
activeLabelID: number;
activeObjectType: ObjectType;
Expand Down

0 comments on commit d9fe712

Please sign in to comment.