diff --git a/cvat-canvas3d/src/typescript/canvas3dModel.ts b/cvat-canvas3d/src/typescript/canvas3dModel.ts index 359d64a5ae8..41c566cf8d7 100644 --- a/cvat-canvas3d/src/typescript/canvas3dModel.ts +++ b/cvat-canvas3d/src/typescript/canvas3dModel.ts @@ -91,6 +91,7 @@ export enum Mode { INTERACT = 'interact', DRAG_CANVAS = 'drag_canvas', GROUP = 'group', + BUSY = 'busy', } export interface Canvas3dDataModel { @@ -102,6 +103,7 @@ export interface Canvas3dDataModel { imageSize: Size; drawData: DrawData; mode: Mode; + objectUpdating: boolean; exception: Error | null; objects: any[]; groupedObjects: any[]; @@ -140,6 +142,7 @@ export class Canvas3dModelImpl extends MasterImpl implements Canvas3dModel { height: 0, width: 0, }, + objectUpdating: false, objects: [], groupedObjects: [], image: null, @@ -179,13 +182,16 @@ export class Canvas3dModelImpl extends MasterImpl implements Canvas3dModel { throw Error(`Canvas is busy. Action: ${this.data.mode}`); } } - - if ([Mode.EDIT].includes(this.data.mode)) { + if ([Mode.EDIT, Mode.BUSY].includes(this.data.mode)) { return; } if (frameData.number === this.data.imageID) { + if (this.data.objectUpdating) { + return; + } this.data.objects = objectStates; + this.data.objectUpdating = true; this.notify(UpdateReasons.OBJECTS_UPDATED); return; } @@ -228,16 +234,16 @@ export class Canvas3dModelImpl extends MasterImpl implements Canvas3dModel { } public isAbleToChangeFrame(): boolean { - const isUnable = [Mode.DRAG, Mode.EDIT, Mode.RESIZE, Mode.INTERACT].includes(this.data.mode) + const isUnable = [Mode.DRAG, Mode.EDIT, Mode.RESIZE, Mode.INTERACT, Mode.BUSY].includes(this.data.mode) || (this.data.mode === Mode.DRAW && typeof this.data.drawData.redraw === 'number'); return !isUnable; } public draw(drawData: DrawData): void { - if (drawData.enabled && this.data.drawData.enabled) { + if (drawData.enabled && this.data.drawData.enabled && !drawData.initialState) { throw new Error('Drawing has been already started'); } - if ([Mode.DRAW, Mode.EDIT].includes(this.data.mode)) { + if ([Mode.DRAW, Mode.EDIT].includes(this.data.mode) && !drawData.initialState) { return; } this.data.drawData.enabled = drawData.enabled; @@ -318,6 +324,9 @@ export class Canvas3dModelImpl extends MasterImpl implements Canvas3dModel { } public configureShapes(shapeProperties: ShapeProperties): void { + this.data.drawData.enabled = false; + this.data.mode = Mode.IDLE; + this.cancel(); this.data.shapeProperties = { ...shapeProperties, }; diff --git a/cvat-canvas3d/src/typescript/canvas3dView.ts b/cvat-canvas3d/src/typescript/canvas3dView.ts index d3703f66e74..5f450ac57db 100644 --- a/cvat-canvas3d/src/typescript/canvas3dView.ts +++ b/cvat-canvas3d/src/typescript/canvas3dView.ts @@ -111,6 +111,8 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { }, }; this.action = { + loading: false, + oldState: '', scan: null, selectable: true, frameCoordinates: { @@ -216,6 +218,35 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { }), ); } + if (this.model.mode === Mode.DRAW && e.ctrlKey && this.model.data.drawData.initialState) { + const { x, y, z } = this.cube.perspective.position; + const { x: width, y: height, z: depth } = this.cube.perspective.scale; + const { x: rotationX, y: rotationY, z: rotationZ } = this.cube.perspective.rotation; + const points = [x, y, z, rotationX, rotationY, rotationZ, width, height, depth, 0, 0, 0, 0, 0, 0, 0]; + const initState = this.model.data.drawData.initialState; + let label; + if (initState) { + ({ label } = initState); + } + this.dispatchEvent( + new CustomEvent('canvas.drawn', { + bubbles: false, + cancelable: true, + detail: { + state: { + ...initState, + shapeType: 'cuboid', + frame: this.model.data.imageID, + points, + label, + }, + continue: true, + duration: 0, + }, + }), + ); + this.action.oldState = Mode.DRAW; + } }); canvasTopView.addEventListener('mousedown', this.startAction.bind(this, 'top')); @@ -293,7 +324,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { this.views.perspective.scene.children[0].children, false, ); - if (intersects.length !== 0) { + if (intersects.length !== 0 || this.controller.focused.clientID !== null) { this.setDefaultZoom(); } else { const { x, y, z } = this.action.frameCoordinates; @@ -417,6 +448,12 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { viewType.controls.mouseButtons.right = CameraControls.ACTION.NONE; } else { viewType.controls = new CameraControls(viewType.camera, viewType.renderer.domElement); + viewType.controls.mouseButtons.left = CameraControls.ACTION.NONE; + viewType.controls.mouseButtons.right = CameraControls.ACTION.NONE; + viewType.controls.mouseButtons.wheel = CameraControls.ACTION.NONE; + viewType.controls.touches.one = CameraControls.ACTION.NONE; + viewType.controls.touches.two = CameraControls.ACTION.NONE; + viewType.controls.touches.three = CameraControls.ACTION.NONE; } viewType.controls.minDistance = CONST.MIN_DISTANCE; viewType.controls.maxDistance = CONST.MAX_DISTANCE; @@ -493,6 +530,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { private startAction(view: any, event: MouseEvent): void { if (event.detail !== 1) return; + if (this.model.mode === Mode.DRAG_CANVAS) return; const { clientID } = this.model.data.activeElement; if (clientID === 'null') return; const canvas = this.views[view as keyof Views].renderer.domElement; @@ -517,6 +555,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { private moveAction(view: any, event: MouseEvent): void { event.preventDefault(); + if (this.model.mode === Mode.DRAG_CANVAS) return; const { clientID } = this.model.data.activeElement; if (clientID === 'null') return; const canvas = this.views[view as keyof Views].renderer.domElement; @@ -571,10 +610,13 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { recentMouseVector: new THREE.Vector2(0, 0), }, }; + this.model.mode = Mode.IDLE; + this.action.selectable = true; } private completeActions(): void { const { scan, detected } = this.action; + if (this.model.mode === Mode.DRAG_CANVAS) return; if (!detected) { this.resetActions(); return; @@ -604,8 +646,6 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { this.adjustPerspectiveCameras(); this.translateReferencePlane(new THREE.Vector3(x, y, z)); this.resetActions(); - this.model.mode = Mode.IDLE; - this.action.selectable = true; } private onGroupDone(objects?: any[]): void { @@ -718,6 +758,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { const object = this.model.data.objects[i]; this.setupObject(object, true); } + this.action.loading = false; } } @@ -735,6 +776,13 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { public notify(model: Canvas3dModel & Master, reason: UpdateReasons): void { if (reason === UpdateReasons.IMAGE_CHANGED) { if (!model.data.image) return; + this.dispatchEvent(new CustomEvent('canvas.canceled')); + if (this.model.mode === Mode.DRAW) { + this.model.data.drawData.enabled = false; + } + this.views.perspective.renderer.dispose(); + this.model.mode = Mode.BUSY; + this.action.loading = true; const loader = new PCDLoader(); const objectURL = URL.createObjectURL(model.data.image.imageData); this.clearScene(); @@ -766,12 +814,21 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { this.setupObjects(); } else if (reason === UpdateReasons.DRAG_CANVAS) { this.dispatchEvent( - new CustomEvent(this.mode === Mode.DRAG_CANVAS ? 'canvas.dragstart' : 'canvas.dragstop', { + new CustomEvent(this.model.mode === Mode.DRAG_CANVAS ? 'canvas.dragstart' : 'canvas.dragstop', { bubbles: false, cancelable: true, }), ); this.model.data.activeElement.clientID = 'null'; + if (this.model.mode === Mode.DRAG_CANVAS) { + const { controls } = this.views.perspective; + controls.mouseButtons.left = CameraControls.ACTION.ROTATE; + controls.mouseButtons.right = CameraControls.ACTION.TRUCK; + controls.mouseButtons.wheel = CameraControls.ACTION.DOLLY; + controls.touches.one = CameraControls.ACTION.TOUCH_ROTATE; + controls.touches.two = CameraControls.ACTION.TOUCH_DOLLY_TRUCK; + controls.touches.three = CameraControls.ACTION.TOUCH_TRUCK; + } this.setupObjects(); } else if (reason === UpdateReasons.CANCEL) { if (this.mode === Mode.DRAW) { @@ -783,7 +840,14 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { } this.model.data.groupData.grouped = []; this.setHelperVisibility(false); - this.mode = Mode.IDLE; + this.model.mode = Mode.IDLE; + const { controls } = this.views.perspective; + controls.mouseButtons.left = CameraControls.ACTION.NONE; + controls.mouseButtons.right = CameraControls.ACTION.NONE; + controls.mouseButtons.wheel = CameraControls.ACTION.NONE; + controls.touches.one = CameraControls.ACTION.NONE; + controls.touches.two = CameraControls.ACTION.NONE; + controls.touches.three = CameraControls.ACTION.NONE; this.dispatchEvent(new CustomEvent('canvas.canceled')); } else if (reason === UpdateReasons.FITTED_CANVAS) { this.dispatchEvent(new CustomEvent('canvas.fit')); @@ -1153,6 +1217,8 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { } this.updateRotationHelperPos(); this.updateResizeHelperPos(); + } else { + this.resetActions(); } } } @@ -1166,6 +1232,16 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { this.action.detachCam = false; } } + if (this.model.mode === Mode.BUSY && !this.action.loading) { + if (this.action.oldState !== '') { + 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; + } } private adjustPerspectiveCameras(): void { @@ -1729,9 +1805,6 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { true, ); if (intersectsBox.length !== 0) { - // const [state] = this.model.data.objects.filter( - // (_state: any): boolean => _state.clientID === Number(this.model.data.selected[view].name), - // ); if (state.pinned) return; this.action.translation.helper = viewType.rayCaster.mouseVector.clone(); this.action.translation.inverseMatrix = intersectsBox[0].object.parent.matrixWorld.invert(); @@ -1749,26 +1822,24 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { public keyControls(key: any): void { const { controls } = this.views.perspective; if (!controls) return; - switch (key.code) { - case CameraAction.ROTATE_RIGHT: - controls.rotate(0.1 * THREE.MathUtils.DEG2RAD * this.speed, 0, true); - break; - case CameraAction.ROTATE_LEFT: - controls.rotate(-0.1 * THREE.MathUtils.DEG2RAD * this.speed, 0, true); - break; - case CameraAction.TILT_UP: - controls.rotate(0, -0.05 * THREE.MathUtils.DEG2RAD * this.speed, true); - break; - case CameraAction.TILT_DOWN: - controls.rotate(0, 0.05 * THREE.MathUtils.DEG2RAD * this.speed, true); - break; - case 'ControlLeft': - this.action.selectable = !key.ctrlKey; - break; - default: - break; - } - if (key.altKey === true) { + if (key.shiftKey) { + switch (key.code) { + case CameraAction.ROTATE_RIGHT: + controls.rotate(0.1 * THREE.MathUtils.DEG2RAD * this.speed, 0, true); + break; + case CameraAction.ROTATE_LEFT: + controls.rotate(-0.1 * THREE.MathUtils.DEG2RAD * this.speed, 0, true); + break; + case CameraAction.TILT_UP: + controls.rotate(0, -0.05 * THREE.MathUtils.DEG2RAD * this.speed, true); + break; + case CameraAction.TILT_DOWN: + controls.rotate(0, 0.05 * THREE.MathUtils.DEG2RAD * this.speed, true); + break; + default: + break; + } + } else if (key.altKey === true) { switch (key.code) { case CameraAction.ZOOM_IN: controls.dolly(CONST.DOLLY_FACTOR, true); @@ -1791,6 +1862,8 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { default: break; } + } else if (key.code === 'ControlLeft') { + this.action.selectable = !key.ctrlKey; } } diff --git a/cvat-canvas3d/src/typescript/consts.ts b/cvat-canvas3d/src/typescript/consts.ts index 3caf834a182..f4bda8fc969 100644 --- a/cvat-canvas3d/src/typescript/consts.ts +++ b/cvat-canvas3d/src/typescript/consts.ts @@ -6,7 +6,7 @@ const BASE_GRID_WIDTH = 2; const MOVEMENT_FACTOR = 200; const DOLLY_FACTOR = 5; const MAX_DISTANCE = 100; -const MIN_DISTANCE = 0; +const MIN_DISTANCE = 0.3; const ZOOM_FACTOR = 7; const ROTATION_HELPER_OFFSET = 0.1; const CAMERA_REFERENCE = 'camRef'; diff --git a/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper3D.tsx b/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper3D.tsx index af0bd291abc..08ed0f1eaf7 100644 --- a/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper3D.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper3D.tsx @@ -30,6 +30,7 @@ interface Props { outlined: boolean; outlineColor: string; colorBy: ColorBy; + frameFetching: boolean; canvasInstance: Canvas3d | Canvas; jobInstance: any; frameData: any; @@ -186,6 +187,7 @@ const CanvasWrapperComponent = (props: Props): ReactElement => { activeObjectType, onShapeDrawn, onCreateAnnotations, + frameFetching, } = props; const { canvasInstance } = props as { canvasInstance: Canvas3d }; @@ -398,16 +400,16 @@ const CanvasWrapperComponent = (props: Props): ReactElement => { }; }, [frameData, annotations, activeLabelID, contextMenuVisibility]); - const screenKeyControl = (code: CameraAction): void => { - canvasInstance.keyControls(new KeyboardEvent('keydown', { code, altKey: true })); + const screenKeyControl = (code: CameraAction, altKey: boolean, shiftKey: boolean): void => { + canvasInstance.keyControls(new KeyboardEvent('keydown', { code, altKey, shiftKey })); }; const ArrowGroup = (): ReactElement => ( - +
- + - + - +