Skip to content

Commit bf6f550

Browse files
authored
React UI: cuboids (cvat-ai#1451)
1 parent 227ab05 commit bf6f550

31 files changed

+2009
-63
lines changed

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,9 @@ __pycache__
2323
# Ignore development npm files
2424
node_modules
2525

26+
# Ignore npm logs file
27+
npm-debug.log*
28+
yarn-debug.log*
29+
yarn-error.log*
30+
2631
.DS_Store

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
- Re-Identification algorithm to merging bounding boxes automatically to the new UI (<https://github.com/opencv/cvat/pull/1406>)
1010
- Methods ``import`` and ``export`` to import/export raw annotations for Job and Task in ``cvat-core`` (<https://github.com/opencv/cvat/pull/1406>)
1111
- Versioning of client packages (``cvat-core``, ``cvat-canvas``, ``cvat-ui``). Initial versions are set to 1.0.0 (<https://github.com/opencv/cvat/pull/1448>)
12+
- Cuboids feature was migrated from old UI to new one. (<https://github.com/opencv/cvat/pull/1451>)
1213

1314
### Changed
1415
-

cvat-canvas/src/scss/canvas.scss

+2-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ polyline.cvat_canvas_shape_splitting {
134134
cursor: nwse-resize;
135135
}
136136

137-
.svg_select_points_l:hover, .svg_select_points_r:hover {
137+
.svg_select_points_l:hover, .svg_select_points_r:hover,
138+
.svg_select_points_ew:hover {
138139
cursor: ew-resize;
139140
}
140141

cvat-canvas/src/typescript/canvasModel.ts

+4
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export interface Configuration {
5050
autoborders?: boolean;
5151
displayAllText?: boolean;
5252
undefinedAttrValue?: string;
53+
showProjections?: boolean;
5354
}
5455

5556
export interface DrawData {
@@ -527,6 +528,9 @@ export class CanvasModelImpl extends MasterImpl implements CanvasModel {
527528
this.data.configuration.displayAllText = configuration.displayAllText;
528529
}
529530

531+
if (typeof (configuration.showProjections) !== 'undefined') {
532+
this.data.configuration.showProjections = configuration.showProjections;
533+
}
530534
if (typeof (configuration.autoborders) !== 'undefined') {
531535
this.data.configuration.autoborders = configuration.autoborders;
532536
}

cvat-canvas/src/typescript/canvasView.ts

+95-13
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,10 @@ export class CanvasViewImpl implements CanvasView, Listener {
437437
.filter((_state: any): boolean => (
438438
_state.clientID === self.activeElement.clientID
439439
));
440+
if (['cuboid', 'rectangle'].includes(state.shapeType)) {
441+
e.preventDefault();
442+
return;
443+
}
440444
if (e.ctrlKey) {
441445
const { points } = state;
442446
self.onEditDone(
@@ -721,7 +725,10 @@ export class CanvasViewImpl implements CanvasView, Listener {
721725
public notify(model: CanvasModel & Master, reason: UpdateReasons): void {
722726
this.geometry = this.controller.geometry;
723727
if (reason === UpdateReasons.CONFIG_UPDATED) {
728+
const { activeElement } = this;
729+
this.deactivate();
724730
this.configuration = model.configuration;
731+
this.activate(activeElement);
725732
this.editHandler.configurate(this.configuration);
726733
this.drawHandler.configurate(this.configuration);
727734

@@ -939,26 +946,63 @@ export class CanvasViewImpl implements CanvasView, Listener {
939946
for (const state of states) {
940947
if (state.hidden || state.outside) continue;
941948
ctx.fillStyle = 'white';
942-
if (['rectangle', 'polygon'].includes(state.shapeType)) {
943-
const points = state.shapeType === 'rectangle' ? [
944-
state.points[0], // xtl
945-
state.points[1], // ytl
946-
state.points[2], // xbr
947-
state.points[1], // ytl
948-
state.points[2], // xbr
949-
state.points[3], // ybr
950-
state.points[0], // xtl
951-
state.points[3], // ybr
952-
] : state.points;
949+
if (['rectangle', 'polygon', 'cuboid'].includes(state.shapeType)) {
950+
let points = [];
951+
if (state.shapeType === 'rectangle') {
952+
points = [
953+
state.points[0], // xtl
954+
state.points[1], // ytl
955+
state.points[2], // xbr
956+
state.points[1], // ytl
957+
state.points[2], // xbr
958+
state.points[3], // ybr
959+
state.points[0], // xtl
960+
state.points[3], // ybr
961+
];
962+
} else if (state.shapeType === 'cuboid') {
963+
points = [
964+
state.points[0],
965+
state.points[1],
966+
state.points[4],
967+
state.points[5],
968+
state.points[8],
969+
state.points[9],
970+
state.points[12],
971+
state.points[13],
972+
];
973+
} else {
974+
points = [...state.points];
975+
}
953976
ctx.beginPath();
954977
ctx.moveTo(points[0], points[1]);
955978
for (let i = 0; i < points.length; i += 2) {
956979
ctx.lineTo(points[i], points[i + 1]);
957980
}
958981
ctx.closePath();
982+
ctx.fill();
959983
}
960984

961-
ctx.fill();
985+
if (state.shapeType === 'cuboid') {
986+
for (let i = 0; i < 5; i++) {
987+
const points = [
988+
state.points[(0 + i * 4) % 16],
989+
state.points[(1 + i * 4) % 16],
990+
state.points[(2 + i * 4) % 16],
991+
state.points[(3 + i * 4) % 16],
992+
state.points[(6 + i * 4) % 16],
993+
state.points[(7 + i * 4) % 16],
994+
state.points[(4 + i * 4) % 16],
995+
state.points[(5 + i * 4) % 16],
996+
];
997+
ctx.beginPath();
998+
ctx.moveTo(points[0], points[1]);
999+
for (let j = 0; j < points.length; j += 2) {
1000+
ctx.lineTo(points[j], points[j + 1]);
1001+
}
1002+
ctx.closePath();
1003+
ctx.fill();
1004+
}
1005+
}
9621006
}
9631007
}
9641008
}
@@ -1055,7 +1099,9 @@ export class CanvasViewImpl implements CanvasView, Listener {
10551099
return `${acc}${val},`;
10561100
}, '',
10571101
);
1058-
(shape as any).clear();
1102+
if (state.shapeType !== 'cuboid') {
1103+
(shape as any).clear();
1104+
}
10591105
shape.attr('points', stringified);
10601106

10611107
if (state.shapeType === 'points' && !isInvisible) {
@@ -1116,6 +1162,9 @@ export class CanvasViewImpl implements CanvasView, Listener {
11161162
} else if (state.shapeType === 'points') {
11171163
this.svgShapes[state.clientID] = this
11181164
.addPoints(stringified, state);
1165+
} else if (state.shapeType === 'cuboid') {
1166+
this.svgShapes[state.clientID] = this
1167+
.addCuboid(stringified, state);
11191168
}
11201169
}
11211170

@@ -1202,6 +1251,10 @@ export class CanvasViewImpl implements CanvasView, Listener {
12021251
this.selectize(false, shape);
12031252
}
12041253

1254+
if (drawnState.shapeType === 'cuboid') {
1255+
(shape as any).attr('projections', false);
1256+
}
1257+
12051258
(shape as any).off('resizestart');
12061259
(shape as any).off('resizing');
12071260
(shape as any).off('resizedone');
@@ -1281,6 +1334,11 @@ export class CanvasViewImpl implements CanvasView, Listener {
12811334
this.content.append(shape.node);
12821335
}
12831336

1337+
const { showProjections } = this.configuration;
1338+
if (state.shapeType === 'cuboid' && showProjections) {
1339+
(shape as any).attr('projections', true);
1340+
}
1341+
12841342
if (!state.pinned) {
12851343
shape.addClass('cvat_canvas_shape_draggable');
12861344
(shape as any).draggable().on('dragstart', (): void => {
@@ -1548,6 +1606,30 @@ export class CanvasViewImpl implements CanvasView, Listener {
15481606
return polyline;
15491607
}
15501608

1609+
private addCuboid(points: string, state: any): any {
1610+
const cube = (this.adoptedContent as any).cube(points)
1611+
.fill(state.color).attr({
1612+
clientID: state.clientID,
1613+
'color-rendering': 'optimizeQuality',
1614+
id: `cvat_canvas_shape_${state.clientID}`,
1615+
fill: state.color,
1616+
'shape-rendering': 'geometricprecision',
1617+
stroke: state.color,
1618+
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
1619+
'data-z-order': state.zOrder,
1620+
}).addClass('cvat_canvas_shape');
1621+
1622+
if (state.occluded) {
1623+
cube.addClass('cvat_canvas_shape_occluded');
1624+
}
1625+
1626+
if (state.hidden || state.outside) {
1627+
cube.style('display', 'none');
1628+
}
1629+
1630+
return cube;
1631+
}
1632+
15511633
private setupPoints(basicPolyline: SVG.PolyLine, state: any): any {
15521634
this.selectize(true, basicPolyline);
15531635

cvat-canvas/src/typescript/consts.ts

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const AREA_THRESHOLD = 9;
1010
const SIZE_THRESHOLD = 3;
1111
const POINTS_STROKE_WIDTH = 1.5;
1212
const POINTS_SELECTED_STROKE_WIDTH = 4;
13+
const MIN_EDGE_LENGTH = 3;
1314
const UNDEFINED_ATTRIBUTE_VALUE = '__undefined__';
1415

1516
export default {
@@ -21,5 +22,6 @@ export default {
2122
SIZE_THRESHOLD,
2223
POINTS_STROKE_WIDTH,
2324
POINTS_SELECTED_STROKE_WIDTH,
25+
MIN_EDGE_LENGTH,
2426
UNDEFINED_ATTRIBUTE_VALUE,
2527
};

0 commit comments

Comments
 (0)