Multiple fixes in 3D drawing, see description (#5410)

### Motivation and context
Related #3438 

- When dblclick one of sideviews during drawing - UI is broken
- When mouseleave sideviews during drawing - bbox disappears, need to
draw again
- Sometimes multiple draw boxes exist on view

Example:

![bug_1](https://user-images.githubusercontent.com/40690378/205139685-2c348b4f-30c0-4bcc-a101-e07c880ff358.gif)
main
Boris Sekachev 3 years ago committed by GitHub
parent 9b0d963d1a
commit d4788ee61e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -83,6 +83,7 @@ non-ascii paths while adding files from "Connected file share" (issue #4428)
- Fixed job exporting (<https://github.com/opencv/cvat/pull/5282>) - Fixed job exporting (<https://github.com/opencv/cvat/pull/5282>)
- Visibility and ignored information fail to be loaded (MOT dataset format) (<https://github.com/opencv/cvat/pull/5270>) - Visibility and ignored information fail to be loaded (MOT dataset format) (<https://github.com/opencv/cvat/pull/5270>)
- Added force logout on CVAT app start if token is missing (<https://github.com/opencv/cvat/pull/5331>) - Added force logout on CVAT app start if token is missing (<https://github.com/opencv/cvat/pull/5331>)
- Drawing issues on 3D canvas (<https://github.com/opencv/cvat/pull/5410>)
- Missed token with using social account authentication (<https://github.com/opencv/cvat/pull/5344>) - Missed token with using social account authentication (<https://github.com/opencv/cvat/pull/5344>)
- An exception when run export for an empty task (<https://github.com/opencv/cvat/pull/5396>) - An exception when run export for an empty task (<https://github.com/opencv/cvat/pull/5396>)
- Fixed FBRS serverless function runtime error on images with alpha channel (<https://github.com/opencv/cvat/pull/5384>) - Fixed FBRS serverless function runtime error on images with alpha channel (<https://github.com/opencv/cvat/pull/5384>)

@ -1,6 +1,6 @@
{ {
"name": "cvat-canvas3d", "name": "cvat-canvas3d",
"version": "0.0.3", "version": "0.0.4",
"description": "Part of Computer Vision Annotation Tool which presents its canvas3D library", "description": "Part of Computer Vision Annotation Tool which presents its canvas3D library",
"main": "src/canvas3d.ts", "main": "src/canvas3d.ts",
"scripts": { "scripts": {

@ -101,7 +101,7 @@ class Canvas3dImpl implements Canvas3d {
} }
public activate(clientID: number | null, attributeID: number | null = null): void { public activate(clientID: number | null, attributeID: number | null = null): void {
this.model.activate(String(clientID), attributeID); this.model.activate(typeof clientID === 'number' ? String(clientID) : null, attributeID);
} }
public fit(): void { public fit(): void {

@ -110,7 +110,6 @@ export interface Canvas3dDataModel {
imageIsDeleted: boolean; imageIsDeleted: boolean;
drawData: DrawData; drawData: DrawData;
mode: Mode; mode: Mode;
objectUpdating: boolean;
exception: Error | null; exception: Error | null;
objects: any[]; objects: any[];
groupedObjects: any[]; groupedObjects: any[];
@ -154,7 +153,6 @@ export class Canvas3dModelImpl extends MasterImpl implements Canvas3dModel {
height: 0, height: 0,
width: 0, width: 0,
}, },
objectUpdating: false,
objects: [], objects: [],
groupedObjects: [], groupedObjects: [],
image: null, image: null,
@ -203,11 +201,7 @@ export class Canvas3dModelImpl extends MasterImpl implements Canvas3dModel {
} }
if (frameData.number === this.data.imageID && frameData.deleted === this.data.imageIsDeleted) { if (frameData.number === this.data.imageID && frameData.deleted === this.data.imageIsDeleted) {
if (this.data.objectUpdating) {
return;
}
this.data.objects = objectStates; this.data.objects = objectStates;
this.data.objectUpdating = true;
this.notify(UpdateReasons.OBJECTS_UPDATED); this.notify(UpdateReasons.OBJECTS_UPDATED);
return; return;
} }

@ -133,7 +133,6 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener {
this.action = { this.action = {
loading: false, loading: false,
oldState: '',
scan: null, scan: null,
selectable: true, selectable: true,
frameCoordinates: { frameCoordinates: {
@ -255,9 +254,10 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener {
cancelable: true, cancelable: true,
detail: { detail: {
state: { state: {
...initState, attributes: { ...initState.attributes },
shapeType: 'cuboid', shapeType: 'cuboid',
frame: this.model.data.imageID, frame: this.model.data.imageID,
group: initState.group?.id || null,
points, points,
label, label,
}, },
@ -266,7 +266,6 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener {
}, },
}), }),
); );
this.action.oldState = Mode.DRAW;
} }
}); });
@ -508,7 +507,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener {
} }
private setDefaultZoom(): void { private setDefaultZoom(): void {
if (this.model.data.activeElement === 'null') { if (this.model.data.activeElement === null) {
Object.keys(this.views).forEach((view: string): void => { Object.keys(this.views).forEach((view: string): void => {
const viewType = this.views[view as keyof Views]; const viewType = this.views[view as keyof Views];
if (view !== ViewType.PERSPECTIVE) { if (view !== ViewType.PERSPECTIVE) {
@ -580,7 +579,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener {
if (event.detail !== 1) return; if (event.detail !== 1) return;
if (this.model.mode === Mode.DRAG_CANVAS) return; if (this.model.mode === Mode.DRAG_CANVAS) return;
const { clientID } = this.model.data.activeElement; const { clientID } = this.model.data.activeElement;
if (clientID === 'null') return; if (clientID === null) return;
const canvas = this.views[view as keyof Views].renderer.domElement; const canvas = this.views[view as keyof Views].renderer.domElement;
const rect = canvas.getBoundingClientRect(); const rect = canvas.getBoundingClientRect();
const { mouseVector } = this.views[view as keyof Views].rayCaster as { mouseVector: THREE.Vector2 }; const { mouseVector } = this.views[view as keyof Views].rayCaster as { mouseVector: THREE.Vector2 };
@ -605,7 +604,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener {
event.preventDefault(); event.preventDefault();
if (this.model.mode === Mode.DRAG_CANVAS) return; if (this.model.mode === Mode.DRAG_CANVAS) return;
const { clientID } = this.model.data.activeElement; const { clientID } = this.model.data.activeElement;
if (clientID === 'null') return; if (clientID === null) return;
const canvas = this.views[view as keyof Views].renderer.domElement; const canvas = this.views[view as keyof Views].renderer.domElement;
const rect = canvas.getBoundingClientRect(); const rect = canvas.getBoundingClientRect();
const { mouseVector } = this.views[view as keyof Views].rayCaster as { mouseVector: THREE.Vector2 }; const { mouseVector } = this.views[view as keyof Views].rayCaster as { mouseVector: THREE.Vector2 };
@ -664,7 +663,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener {
private completeActions(): void { private completeActions(): void {
const { scan, detected } = this.action; const { scan, detected } = this.action;
if (this.model.mode === Mode.DRAG_CANVAS) return; if (this.model.data.activeElement.clientID === null) return;
if (!detected) { if (!detected) {
this.resetActions(); this.resetActions();
return; return;
@ -807,6 +806,11 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener {
this.setupObject(object, true); this.setupObject(object, true);
} }
this.action.loading = false; this.action.loading = false;
if (this.mode === Mode.DRAW) {
// if setupObjects was called during drawing, need to restore drawable object
this.views.perspective.scene.children[0].add(this.cube.perspective);
}
} }
} }
@ -871,23 +875,33 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener {
} else if (reason === UpdateReasons.SHAPE_ACTIVATED) { } else if (reason === UpdateReasons.SHAPE_ACTIVATED) {
const { clientID } = this.model.data.activeElement; const { clientID } = this.model.data.activeElement;
this.setupObjects(); this.setupObjects();
if (clientID !== 'null') { if (clientID !== null) {
this.setDefaultZoom(); this.setDefaultZoom();
} }
} else if (reason === UpdateReasons.DRAW) { } else if (reason === UpdateReasons.DRAW) {
const data: DrawData = this.controller.drawData; const data: DrawData = this.controller.drawData;
this.cube = new CuboidModel('line', '#ffffff');
if (data.redraw) { if (data.redraw) {
const object = this.views.perspective.scene.getObjectByName(String(data.redraw)); const object = this.views.perspective.scene.getObjectByName(String(data.redraw));
if (object) { if (object) {
this.cube.perspective = object.clone() as THREE.Mesh; this.cube.perspective = object.clone() as THREE.Mesh;
object.visible = false;
} }
} else if (data.initialState) { } else if (data.initialState) {
this.model.data.activeElement.clientID = 'null';
this.setupObjects();
this.cube = this.setupObject(data.initialState, false); this.cube = this.setupObject(data.initialState, false);
} else {
this.cube = new CuboidModel('line', '#ffffff');
}
this.cube.setName('drawTemplate');
this.model.data.activeElement.clientID = null;
this.setupObjects();
if (data.redraw) {
const object = this.views.perspective.scene.getObjectByName(String(data.redraw));
if (object) {
object.visible = false;
}
} }
this.setHelperVisibility(false); this.setHelperVisibility(false);
} else if (reason === UpdateReasons.OBJECTS_UPDATED) { } else if (reason === UpdateReasons.OBJECTS_UPDATED) {
this.setupObjects(); this.setupObjects();
@ -899,7 +913,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener {
cancelable: true, cancelable: true,
}), }),
); );
this.model.data.activeElement.clientID = 'null'; this.model.data.activeElement.clientID = null;
this.setupObjects(); this.setupObjects();
} else if (reason === UpdateReasons.CANCEL) { } else if (reason === UpdateReasons.CANCEL) {
if (this.mode === Mode.DRAG_CANVAS) { if (this.mode === Mode.DRAG_CANVAS) {
@ -933,7 +947,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener {
this.onGroupDone(this.model.data.groupData.grouped); this.onGroupDone(this.model.data.groupData.grouped);
} else { } else {
this.model.data.groupData.grouped = []; this.model.data.groupData.grouped = [];
this.model.data.activeElement.clientID = 'null'; this.model.data.activeElement.clientID = null;
this.setupObjects(); this.setupObjects();
} }
} }
@ -1235,15 +1249,11 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener {
private renderRayCaster = (viewType: RenderView): void => { private renderRayCaster = (viewType: RenderView): void => {
viewType.rayCaster.renderer.setFromCamera(viewType.rayCaster.mouseVector, viewType.camera); viewType.rayCaster.renderer.setFromCamera(viewType.rayCaster.mouseVector, viewType.camera);
if (this.mode === Mode.DRAW) { if (this.mode === Mode.DRAW) {
const intersects = this.views.perspective.rayCaster.renderer.intersectObjects( const [intersection] = viewType.rayCaster.renderer.intersectObjects(this.views.perspective.scene.children);
this.views.perspective.scene.children, if (intersection) {
false, const object = this.views.perspective.scene.getObjectByName('drawTemplate');
); const { x, y, z } = intersection.point;
if (intersects.length > 0) { object.position.set(x, y, z);
this.views.perspective.scene.children[0].add(this.cube.perspective);
const newPoints = intersects[0].point;
this.cube.perspective.position.copy(newPoints);
this.views.perspective.renderer.domElement.style.cursor = 'default';
} }
} else if (this.mode === Mode.IDLE && !this.isPerspectiveBeingDragged) { } else if (this.mode === Mode.IDLE && !this.isPerspectiveBeingDragged) {
const { children } = this.views.perspective.scene.children[0]; const { children } = this.views.perspective.scene.children[0];
@ -1300,7 +1310,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener {
this.renderRayCaster(viewType); this.renderRayCaster(viewType);
} }
const { clientID } = this.model.data.activeElement; const { clientID } = this.model.data.activeElement;
if (clientID !== 'null' && view !== ViewType.PERSPECTIVE) { if (clientID !== null && view !== ViewType.PERSPECTIVE) {
viewType.rayCaster.renderer.setFromCamera(viewType.rayCaster.mouseVector, viewType.camera); viewType.rayCaster.renderer.setFromCamera(viewType.rayCaster.mouseVector, viewType.camera);
// First Scan // First Scan
if (this.action.scan === view) { if (this.action.scan === view) {
@ -1324,6 +1334,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener {
} }
} }
}); });
if (this.action.detachCam && this.action.detachCamRef === this.model.data.activeElement.clientID) { if (this.action.detachCam && this.action.detachCamRef === this.model.data.activeElement.clientID) {
try { try {
this.detachCamera(null); this.detachCamera(null);
@ -1332,15 +1343,9 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener {
this.action.detachCam = false; this.action.detachCam = false;
} }
} }
if (this.model.mode === Mode.BUSY && !this.action.loading) { if (this.model.mode === Mode.BUSY && !this.action.loading) {
if (this.action.oldState !== '') { this.model.mode = Mode.IDLE;
this.model.mode = this.action.oldState;
this.action.oldState = '';
} else {
this.model.mode = Mode.IDLE;
}
} else if (this.model.data.objectUpdating && !this.action.loading) {
this.model.data.objectUpdating = false;
} }
} }

@ -1,4 +1,5 @@
// Copyright (C) 2021-2022 Intel Corporation // Copyright (C) 2021-2022 Intel Corporation
// Copyright (C) 2022 CVAT.ai Corporation
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT

Loading…
Cancel
Save