Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support of ellipses #4062

Merged
merged 23 commits into from
Jan 12, 2022
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- User is able to customize information that text labels show (<https://github.com/openvinotoolkit/cvat/pull/4029>)
- Support for uploading manifest with any name (<https://github.com/openvinotoolkit/cvat/pull/4041>)
- Added information about OpenVINO toolkit to login page (<https://github.com/openvinotoolkit/cvat/pull/4077>)
- Support for working with ellipses (<https://github.com/openvinotoolkit/cvat/pull/4062>)

### Changed
- Users don't have access to a task object anymore if they are assigneed only on some jobs of the task (<https://github.com/openvinotoolkit/cvat/pull/3788>)
Expand Down
4 changes: 2 additions & 2 deletions cvat-canvas/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cvat-canvas/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cvat-canvas",
"version": "2.11.1",
"version": "2.12.0",
"description": "Part of Computer Vision Annotation Tool which presents its canvas library",
"main": "src/canvas.ts",
"scripts": {
Expand Down
4 changes: 4 additions & 0 deletions cvat-canvas/src/typescript/autoborderHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,10 @@ export class AutoborderHandlerImpl implements AutoborderHandler {
let points = '';
if (shape.tagName === 'polyline' || shape.tagName === 'polygon') {
points = shape.getAttribute('points');
} else if (shape.tagName === 'ellipse') {
const cx = +shape.getAttribute('cx');
const cy = +shape.getAttribute('cy');
points = `${cx},${cy}`;
} else if (shape.tagName === 'rect') {
const x = +shape.getAttribute('x');
const y = +shape.getAttribute('y');
Expand Down
98 changes: 73 additions & 25 deletions cvat-canvas/src/typescript/canvasView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
translateToSVG,
translateFromSVG,
translateToCanvas,
translateFromCanvas,
pointsToNumberArray,
parsePoints,
displayShapeSize,
Expand All @@ -33,6 +34,7 @@ import {
ShapeSizeElement,
DrawnState,
rotate2DPoints,
readPointsFromShape,
} from './shared';
import {
CanvasModel,
Expand Down Expand Up @@ -88,7 +90,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
private activeElement: ActiveElement;
private configuration: Configuration;
private snapToAngleResize: number;
private serviceFlags: {
private innerObjectsFlags: {
drawHidden: Record<number, boolean>;
};

Expand All @@ -112,7 +114,7 @@ export class CanvasViewImpl implements CanvasView, Listener {

private translateFromCanvas(points: number[]): number[] {
const { offset } = this.controller.geometry;
return points.map((coord: number): number => coord - offset);
return translateFromCanvas(offset, points);
}

private translatePointsFromRotatedShape(shape: SVG.Shape, points: number[]): number[] {
Expand Down Expand Up @@ -158,12 +160,12 @@ export class CanvasViewImpl implements CanvasView, Listener {
}, '');
}

private isServiceHidden(clientID: number): boolean {
return this.serviceFlags.drawHidden[clientID] || false;
private isInnerHidden(clientID: number): boolean {
return this.innerObjectsFlags.drawHidden[clientID] || false;
}

private setupServiceHidden(clientID: number, value: boolean): void {
this.serviceFlags.drawHidden[clientID] = value;
private setupInnerFlags(clientID: number, path: 'drawHidden', value: boolean): void {
this.innerObjectsFlags[path][clientID] = value;
const shape = this.svgShapes[clientID];
const text = this.svgTexts[clientID];
const state = this.drawnStates[clientID];
Expand All @@ -179,7 +181,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
text.addClass('cvat_canvas_hidden');
}
} else {
delete this.serviceFlags.drawHidden[clientID];
delete this.innerObjectsFlags[path][clientID];

if (state) {
if (!state.outside && !state.hidden) {
Expand Down Expand Up @@ -236,10 +238,11 @@ export class CanvasViewImpl implements CanvasView, Listener {
}

private onDrawDone(data: any | null, duration: number, continueDraw?: boolean): void {
const hiddenBecauseOfDraw = Object.keys(this.serviceFlags.drawHidden).map((_clientID): number => +_clientID);
const hiddenBecauseOfDraw = Object.keys(this.innerObjectsFlags.drawHidden)
.map((_clientID): number => +_clientID);
if (hiddenBecauseOfDraw.length) {
for (const hidden of hiddenBecauseOfDraw) {
this.setupServiceHidden(hidden, false);
this.setupInnerFlags(hidden, 'drawHidden', false);
}
}

Expand Down Expand Up @@ -967,7 +970,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
this.configuration = model.configuration;
this.mode = Mode.IDLE;
this.snapToAngleResize = consts.SNAP_TO_ANGLE_RESIZE_DEFAULT;
this.serviceFlags = {
this.innerObjectsFlags = {
drawHidden: {},
};

Expand Down Expand Up @@ -1367,7 +1370,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
this.canvas.style.cursor = 'crosshair';
this.mode = Mode.DRAW;
if (typeof data.redraw === 'number') {
this.setupServiceHidden(data.redraw, true);
this.setupInnerFlags(data.redraw, 'drawHidden', true);
}
this.drawHandler.draw(data, this.geometry);
} else {
Expand Down Expand Up @@ -1544,6 +1547,12 @@ export class CanvasViewImpl implements CanvasView, Listener {
ctx.fill();
}

if (state.shapeType === 'ellipse') {
const [cx, cy, rightX, topY] = state.points;
ctx.ellipse(cx, cy, rightX - cx, cy - topY, 0, 0, 2 * Math.PI);
ctx.fill();
}

if (state.shapeType === 'cuboid') {
for (let i = 0; i < 5; i++) {
const points = [
Expand Down Expand Up @@ -1596,7 +1605,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
const drawnState = this.drawnStates[clientID];
const shape = this.svgShapes[state.clientID];
const text = this.svgTexts[state.clientID];
const isInvisible = state.hidden || state.outside || this.isServiceHidden(state.clientID);
const isInvisible = state.hidden || state.outside || this.isInnerHidden(state.clientID);

if (drawnState.hidden !== state.hidden || drawnState.outside !== state.outside) {
if (isInvisible) {
Expand Down Expand Up @@ -1659,6 +1668,12 @@ export class CanvasViewImpl implements CanvasView, Listener {
width: xbr - xtl,
height: ybr - ytl,
});
} else if (state.shapeType === 'ellipse') {
const [cx, cy] = translatedPoints;
const [rx, ry] = [translatedPoints[2] - cx, cy - translatedPoints[3]];
shape.attr({
cx, cy, rx, ry,
});
} else {
const stringified = this.stringifyToCanvas(translatedPoints);
if (state.shapeType !== 'cuboid') {
Expand Down Expand Up @@ -1728,6 +1743,8 @@ export class CanvasViewImpl implements CanvasView, Listener {
this.svgShapes[state.clientID] = this.addPolyline(stringified, state);
} else if (state.shapeType === 'points') {
this.svgShapes[state.clientID] = this.addPoints(stringified, state);
} else if (state.shapeType === 'ellipse') {
this.svgShapes[state.clientID] = this.addEllipse(stringified, state);
} else if (state.shapeType === 'cuboid') {
this.svgShapes[state.clientID] = this.addCuboid(stringified, state);
} else {
Expand Down Expand Up @@ -1933,10 +1950,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
if (Math.sqrt(dx2 + dy2) >= delta) {
// these points does not take into account possible transformations, applied on the element
// so, if any (like rotation) we need to map them to canvas coordinate space
let points = pointsToNumberArray(
shape.attr('points') || `${shape.attr('x')},${shape.attr('y')} ` +
`${shape.attr('x') + shape.attr('width')},${shape.attr('y') + shape.attr('height')}`,
);
let points = readPointsFromShape(shape);

// let's keep current points, but they could be rewritten in updateObjects
this.drawnStates[clientID].points = this.translateFromCanvas(points);
Expand Down Expand Up @@ -2021,10 +2035,7 @@ export class CanvasViewImpl implements CanvasView, Listener {

// these points does not take into account possible transformations, applied on the element
// so, if any (like rotation) we need to map them to canvas coordinate space
let points = pointsToNumberArray(
shape.attr('points') || `${shape.attr('x')},${shape.attr('y')} ` +
`${shape.attr('x') + shape.attr('width')},${shape.attr('y') + shape.attr('height')}`,
);
let points = readPointsFromShape(shape);

// let's keep current points, but they could be rewritten in updateObjects
this.drawnStates[clientID].points = this.translateFromCanvas(points);
Expand Down Expand Up @@ -2101,6 +2112,10 @@ export class CanvasViewImpl implements CanvasView, Listener {
// for rectangle finding a center is simple
cx = +shape.attr('x') + +shape.attr('width') / 2;
cy = +shape.attr('y') + +shape.attr('height') / 2;
} else if (shape.type === 'ellipse') {
// even simpler for ellipses
cx = +shape.attr('cx');
cy = +shape.attr('cy');
} else {
// for polyshapes we use special algorithm
const points = parsePoints(pointsToNumberArray(shape.attr('points')));
Expand Down Expand Up @@ -2247,7 +2262,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
rect.addClass('cvat_canvas_shape_occluded');
}

if (state.hidden || state.outside || this.isServiceHidden(state.clientID)) {
if (state.hidden || state.outside || this.isInnerHidden(state.clientID)) {
rect.addClass('cvat_canvas_hidden');
}

Expand All @@ -2273,7 +2288,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
polygon.addClass('cvat_canvas_shape_occluded');
}

if (state.hidden || state.outside || this.isServiceHidden(state.clientID)) {
if (state.hidden || state.outside || this.isInnerHidden(state.clientID)) {
polygon.addClass('cvat_canvas_hidden');
}

Expand All @@ -2299,7 +2314,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
polyline.addClass('cvat_canvas_shape_occluded');
}

if (state.hidden || state.outside || this.isServiceHidden(state.clientID)) {
if (state.hidden || state.outside || this.isInnerHidden(state.clientID)) {
polyline.addClass('cvat_canvas_hidden');
}

Expand All @@ -2326,7 +2341,7 @@ export class CanvasViewImpl implements CanvasView, Listener {
cube.addClass('cvat_canvas_shape_occluded');
}

if (state.hidden || state.outside || this.isServiceHidden(state.clientID)) {
if (state.hidden || state.outside || this.isInnerHidden(state.clientID)) {
cube.addClass('cvat_canvas_hidden');
}

Expand Down Expand Up @@ -2359,6 +2374,39 @@ export class CanvasViewImpl implements CanvasView, Listener {
return group;
}

private addEllipse(points: string, state: any): SVG.Rect {
const [cx, cy, rightX, topY] = points.split(/[/,\s]/g).map((coord) => +coord);
const [rx, ry] = [rightX - cx, cy - topY];
const rect = this.adoptedContent
.ellipse(rx * 2, ry * 2)
.attr({
clientID: state.clientID,
'color-rendering': 'optimizeQuality',
id: `cvat_canvas_shape_${state.clientID}`,
fill: state.color,
'shape-rendering': 'geometricprecision',
stroke: state.color,
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
'data-z-order': state.zOrder,
})
.center(cx, cy)
.addClass('cvat_canvas_shape');

if (state.rotation) {
rect.rotate(state.rotation);
}

if (state.occluded) {
rect.addClass('cvat_canvas_shape_occluded');
}

if (state.hidden || state.outside || this.isInnerHidden(state.clientID)) {
rect.addClass('cvat_canvas_hidden');
}

return rect;
}

private addPoints(points: string, state: any): SVG.PolyLine {
const shape = this.adoptedContent
.polyline(points)
Expand All @@ -2375,7 +2423,7 @@ export class CanvasViewImpl implements CanvasView, Listener {

const group = this.setupPoints(shape, state);

if (state.hidden || state.outside || this.isServiceHidden(state.clientID)) {
if (state.hidden || state.outside || this.isInnerHidden(state.clientID)) {
group.addClass('cvat_canvas_hidden');
}

Expand Down
Loading