Skip to content

Commit 78dad73

Browse files
authored
React UI: Added shortcuts (#1230)
1 parent 8afb5dd commit 78dad73

27 files changed

+1349
-213
lines changed

cvat-canvas/src/typescript/drawHandler.ts

+64-60
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ export class DrawHandlerImpl implements DrawHandler {
3434
private onDrawDone: (data: object, continueDraw?: boolean) => void;
3535
private canvas: SVG.Container;
3636
private text: SVG.Container;
37+
private cursorPosition: {
38+
x: number;
39+
y: number;
40+
};
3741
private crosshair: {
3842
x: SVG.Line;
3943
y: SVG.Line;
@@ -96,12 +100,13 @@ export class DrawHandlerImpl implements DrawHandler {
96100
}
97101

98102
private addCrosshair(): void {
103+
const { x, y } = this.cursorPosition;
99104
this.crosshair = {
100-
x: this.canvas.line(0, 0, this.canvas.node.clientWidth, 0).attr({
105+
x: this.canvas.line(0, y, this.canvas.node.clientWidth, y).attr({
101106
'stroke-width': consts.BASE_STROKE_WIDTH / (2 * this.geometry.scale),
102107
zOrder: Number.MAX_SAFE_INTEGER,
103108
}).addClass('cvat_canvas_crosshair'),
104-
y: this.canvas.line(0, 0, 0, this.canvas.node.clientHeight).attr({
109+
y: this.canvas.line(x, 0, x, this.canvas.node.clientHeight).attr({
105110
'stroke-width': consts.BASE_STROKE_WIDTH / (2 * this.geometry.scale),
106111
zOrder: Number.MAX_SAFE_INTEGER,
107112
}).addClass('cvat_canvas_crosshair'),
@@ -181,7 +186,6 @@ export class DrawHandlerImpl implements DrawHandler {
181186
this.shapeSizeElement.update(this.drawInstance);
182187
}).addClass('cvat_canvas_shape_drawing').attr({
183188
'stroke-width': consts.BASE_STROKE_WIDTH / this.geometry.scale,
184-
z_order: Number.MAX_SAFE_INTEGER,
185189
});
186190
}
187191

@@ -222,10 +226,6 @@ export class DrawHandlerImpl implements DrawHandler {
222226
}
223227

224228
private drawPolyshape(): void {
225-
this.drawInstance.attr({
226-
z_order: Number.MAX_SAFE_INTEGER,
227-
});
228-
229229
let size = this.drawData.numberOfPoints;
230230
const sizeDecrement = function sizeDecrement(): void {
231231
if (!--size) {
@@ -371,18 +371,17 @@ export class DrawHandlerImpl implements DrawHandler {
371371

372372
// Common settings for rectangle and polyshapes
373373
private pasteShape(): void {
374-
this.drawInstance.attr({
375-
z_order: Number.MAX_SAFE_INTEGER,
376-
});
374+
function moveShape(shape: SVG.Shape, x: number, y: number): void {
375+
const bbox = shape.bbox();
376+
shape.move(x - bbox.width / 2, y - bbox.height / 2);
377+
}
377378

378-
this.canvas.on('mousemove.draw', (e: MouseEvent): void => {
379-
const [x, y] = translateToSVG(
380-
this.canvas.node as any as SVGSVGElement,
381-
[e.clientX, e.clientY],
382-
);
379+
const { x: initialX, y: initialY } = this.cursorPosition;
380+
moveShape(this.drawInstance, initialX, initialY);
383381

384-
const bbox = this.drawInstance.bbox();
385-
this.drawInstance.move(x - bbox.width / 2, y - bbox.height / 2);
382+
this.canvas.on('mousemove.draw', (): void => {
383+
const { x, y } = this.cursorPosition; // was computer in another callback
384+
moveShape(this.drawInstance, x, y);
386385
});
387386
}
388387

@@ -429,45 +428,53 @@ export class DrawHandlerImpl implements DrawHandler {
429428
this.pastePolyshape();
430429
}
431430

432-
private pastePoints(points: string): void {
433-
this.drawInstance = (this.canvas as any).polyline(points)
431+
private pastePoints(initialPoints: string): void {
432+
function moveShape(
433+
shape: SVG.PolyLine,
434+
group: SVG.G,
435+
x: number,
436+
y: number,
437+
scale: number,
438+
): void {
439+
const bbox = shape.bbox();
440+
shape.move(x - bbox.width / 2, y - bbox.height / 2);
441+
442+
const points = shape.attr('points').split(' ');
443+
const radius = consts.BASE_POINT_SIZE / scale;
444+
445+
group.children().forEach((child: SVG.Element, idx: number): void => {
446+
const [px, py] = points[idx].split(',');
447+
child.move(px - radius / 2, py - radius / 2);
448+
});
449+
}
450+
451+
const { x: initialX, y: initialY } = this.cursorPosition;
452+
this.pointsGroup = this.canvas.group();
453+
this.drawInstance = (this.canvas as any).polyline(initialPoints)
434454
.addClass('cvat_canvas_shape_drawing').style({
435455
'stroke-width': 0,
436456
});
437457

438-
this.pointsGroup = this.canvas.group();
439-
for (const point of points.split(' ')) {
458+
let numOfPoints = initialPoints.split(' ').length;
459+
while (numOfPoints) {
460+
numOfPoints--;
440461
const radius = consts.BASE_POINT_SIZE / this.geometry.scale;
441462
const stroke = consts.POINTS_STROKE_WIDTH / this.geometry.scale;
442-
const [x, y] = point.split(',').map((coord: string): number => +coord);
443-
this.pointsGroup.circle().move(x - radius / 2, y - radius / 2)
444-
.fill('white').stroke('black').attr({
445-
r: radius,
446-
'stroke-width': stroke,
447-
});
463+
this.pointsGroup.circle().fill('white').stroke('black').attr({
464+
r: radius,
465+
'stroke-width': stroke,
466+
});
448467
}
449468

450-
this.pointsGroup.attr({
451-
z_order: Number.MAX_SAFE_INTEGER,
452-
});
469+
moveShape(
470+
this.drawInstance, this.pointsGroup, initialX, initialY, this.geometry.scale,
471+
);
453472

454-
this.canvas.on('mousemove.draw', (e: MouseEvent): void => {
455-
const [x, y] = translateToSVG(
456-
this.canvas.node as any as SVGSVGElement,
457-
[e.clientX, e.clientY],
473+
this.canvas.on('mousemove.draw', (): void => {
474+
const { x, y } = this.cursorPosition; // was computer in another callback
475+
moveShape(
476+
this.drawInstance, this.pointsGroup, x, y, this.geometry.scale,
458477
);
459-
460-
const bbox = this.drawInstance.bbox();
461-
this.drawInstance.move(x - bbox.width / 2, y - bbox.height / 2);
462-
const radius = consts.BASE_POINT_SIZE / this.geometry.scale;
463-
const newPoints = this.drawInstance.attr('points').split(' ');
464-
if (this.pointsGroup) {
465-
this.pointsGroup.children()
466-
.forEach((child: SVG.Element, idx: number): void => {
467-
const [px, py] = newPoints[idx].split(',');
468-
child.move(px - radius / 2, py - radius / 2);
469-
});
470-
}
471478
});
472479

473480
this.pastePolyshape();
@@ -593,23 +600,20 @@ export class DrawHandlerImpl implements DrawHandler {
593600
this.crosshair = null;
594601
this.drawInstance = null;
595602
this.pointsGroup = null;
603+
this.cursorPosition = {
604+
x: 0,
605+
y: 0,
606+
};
596607

597608
this.canvas.on('mousemove.crosshair', (e: MouseEvent): void => {
609+
const [x, y] = translateToSVG(
610+
this.canvas.node as any as SVGSVGElement,
611+
[e.clientX, e.clientY],
612+
);
613+
this.cursorPosition = { x, y };
598614
if (this.crosshair) {
599-
const [x, y] = translateToSVG(
600-
this.canvas.node as any as SVGSVGElement,
601-
[e.clientX, e.clientY],
602-
);
603-
604-
this.crosshair.x.attr({
605-
y1: y,
606-
y2: y,
607-
});
608-
609-
this.crosshair.y.attr({
610-
x1: x,
611-
x2: x,
612-
});
615+
this.crosshair.x.attr({ y1: y, y2: y });
616+
this.crosshair.y.attr({ x1: x, x2: x });
613617
}
614618
});
615619
}

cvat-core/src/annotations-collection.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -862,7 +862,7 @@
862862
: (frame) => frame - 1;
863863
for (let frame = frameFrom; predicate(frame); frame = update(frame)) {
864864
// First prepare all data for the frame
865-
// Consider all shapes, tags, and tracks that have keyframe here
865+
// Consider all shapes, tags, and not outside tracks that have keyframe here
866866
// In particular consider first and last frame as keyframes for all frames
867867
const statesData = [].concat(
868868
(frame in this.shapes ? this.shapes[frame] : [])
@@ -876,7 +876,10 @@
876876
|| frame === frameFrom
877877
|| frame === frameTo
878878
));
879-
statesData.push(...tracks.map((track) => track.get(frame)));
879+
statesData.push(
880+
...tracks.map((track) => track.get(frame))
881+
.filter((state) => !state.outside),
882+
);
880883

881884
// Nothing to filtering, go to the next iteration
882885
if (!statesData.length) {

cvat-core/src/session.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1343,7 +1343,7 @@
13431343
return annotationsData;
13441344
};
13451345

1346-
Job.prototype.annotations.search.implementation = async function (filters, frameFrom, frameTo) {
1346+
Job.prototype.annotations.search.implementation = function (filters, frameFrom, frameTo) {
13471347
if (!Array.isArray(filters) || filters.some((filter) => typeof (filter) !== 'string')) {
13481348
throw new ArgumentError(
13491349
'The filters argument must be an array of strings',
@@ -1555,7 +1555,7 @@
15551555
return result;
15561556
};
15571557

1558-
Job.prototype.annotations.search.implementation = async function (filters, frameFrom, frameTo) {
1558+
Task.prototype.annotations.search.implementation = function (filters, frameFrom, frameTo) {
15591559
if (!Array.isArray(filters) || filters.some((filter) => typeof (filter) !== 'string')) {
15601560
throw new ArgumentError(
15611561
'The filters argument must be an array of strings',

cvat-ui/package-lock.json

+9-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cvat-ui/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"prop-types": "^15.7.2",
6262
"react": "^16.9.0",
6363
"react-dom": "^16.9.0",
64+
"react-hotkeys": "^2.0.0",
6465
"react-redux": "^7.1.1",
6566
"react-router": "^5.1.0",
6667
"react-router-dom": "^5.1.0",

0 commit comments

Comments
 (0)