diff --git a/cvat-canvas3d/src/typescript/canvas3dModel.ts b/cvat-canvas3d/src/typescript/canvas3dModel.ts index 41c566cf8d74..0575cc1a1613 100644 --- a/cvat-canvas3d/src/typescript/canvas3dModel.ts +++ b/cvat-canvas3d/src/typescript/canvas3dModel.ts @@ -67,11 +67,14 @@ export interface ShapeProperties { outlineColor: string; selectedOpacity: number; colorBy: string; + resetZoom: boolean; + canvasBackgroundColor: string; } export enum UpdateReasons { IMAGE_CHANGED = 'image_changed', OBJECTS_UPDATED = 'objects_updated', + UPDATE_CANVAS_BACKGROUND = 'update_canvas_background', DRAW = 'draw', SELECT = 'select', CANCEL = 'cancel', @@ -80,6 +83,7 @@ export enum UpdateReasons { SHAPE_ACTIVATED = 'shape_activated', GROUP = 'group', FITTED_CANVAS = 'fitted_canvas', + UPDATE_OPACITY = 'update_opacity', } export enum Mode { @@ -172,6 +176,8 @@ export class Canvas3dModelImpl extends MasterImpl implements Canvas3dModel { outlineColor: '#000000', selectedOpacity: 60, colorBy: 'Label', + resetZoom: true, + canvasBackgroundColor: '#000000', }, }; } @@ -324,13 +330,18 @@ export class Canvas3dModelImpl extends MasterImpl implements Canvas3dModel { } public configureShapes(shapeProperties: ShapeProperties): void { + const { opacity, selectedOpacity } = this.data.shapeProperties; this.data.drawData.enabled = false; this.data.mode = Mode.IDLE; this.cancel(); this.data.shapeProperties = { ...shapeProperties, }; - this.notify(UpdateReasons.OBJECTS_UPDATED); + if (opacity !== shapeProperties.opacity || selectedOpacity !== shapeProperties.selectedOpacity) { + this.notify(UpdateReasons.UPDATE_OPACITY); + } else { + this.notify(UpdateReasons.OBJECTS_UPDATED); + } } public fit(): void { diff --git a/cvat-canvas3d/src/typescript/canvas3dView.ts b/cvat-canvas3d/src/typescript/canvas3dView.ts index 5f450ac57dbc..6876d85198b0 100644 --- a/cvat-canvas3d/src/typescript/canvas3dView.ts +++ b/cvat-canvas3d/src/typescript/canvas3dView.ts @@ -204,6 +204,16 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { const canvasSideView = this.views.side.renderer.domElement; const canvasFrontView = this.views.front.renderer.domElement; + canvasPerspectiveView.addEventListener('mouseleave', (event: MouseEvent): void => { + if (this.mode === Mode.DRAW) { + event.preventDefault(); + this.cancelDraw(); + this.setHelperVisibility(false); + this.mode = Mode.IDLE; + this.dispatchEvent(new CustomEvent('canvas.canceled')); + } + }); + canvasPerspectiveView.addEventListener('contextmenu', (e: MouseEvent): void => { if (this.controller.focused.clientID !== null) { this.dispatchEvent( @@ -448,12 +458,6 @@ 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; @@ -479,7 +483,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { { passive: false }, ); }); - + this.dispatchEvent(new CustomEvent('canvas.settings')); model.subscribe(this); } @@ -750,12 +754,27 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { return cuboid; } - private setupObjects(): void { + private setupObjects(selected?: boolean): void { + const { clientID } = this.model.data.activeElement; if (this.views.perspective.scene.children[0]) { - this.clearSceneObjects(); + if (!selected) { + this.clearSceneObjects(); + } + this.resetColor(); this.setHelperVisibility(false); for (let i = 0; i < this.model.data.objects.length; i++) { const object = this.model.data.objects[i]; + if (selected) { + const target = this.views.perspective.scene.getObjectByName(clientID); + if (`${object.clientID}` !== clientID) { + continue; + } else { + this.views.perspective.scene.children[0].remove(target); + this.views.side.scene.children[0].children = []; + this.views.front.scene.children[0].children = []; + this.views.top.scene.children[0].children = []; + } + } this.setupObject(object, true); } this.action.loading = false; @@ -773,6 +792,14 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { this.views.perspective.renderer.domElement.dispatchEvent(event); } + private cancelDraw(): void { + this.controller.drawData.enabled = false; + this.controller.drawData.redraw = undefined; + Object.keys(this.views).forEach((view: string): void => { + this.views[view as keyof Views].scene.children[0].remove(this.cube[view as keyof Views]); + }); + } + public notify(model: Canvas3dModel & Master, reason: UpdateReasons): void { if (reason === UpdateReasons.IMAGE_CHANGED) { if (!model.data.image) return; @@ -789,9 +816,21 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { loader.load(objectURL, this.addScene.bind(this)); URL.revokeObjectURL(objectURL); this.dispatchEvent(new CustomEvent('canvas.setup')); + } else if (reason === UpdateReasons.UPDATE_CANVAS_BACKGROUND) { + const { canvasBackgroundColor } = this.model.data.shapeProperties; + this.views.top.scene.background = new THREE.Color(canvasBackgroundColor); + this.views.perspective.scene.background = new THREE.Color(canvasBackgroundColor); + this.views.side.scene.background = new THREE.Color(canvasBackgroundColor); + this.views.front.scene.background = new THREE.Color(canvasBackgroundColor); + } else if (reason === UpdateReasons.UPDATE_OPACITY) { + this.resetColor(); } else if (reason === UpdateReasons.SHAPE_ACTIVATED) { const { clientID } = this.model.data.activeElement; - this.setupObjects(); + if (clientID !== 'null') { + this.setupObjects(true); + } else { + this.setupObjects(); + } if (clientID !== 'null') { this.setDefaultZoom(); } @@ -820,34 +859,14 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { }), ); 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) { - this.controller.drawData.enabled = false; - this.controller.drawData.redraw = undefined; - Object.keys(this.views).forEach((view: string): void => { - this.views[view as keyof Views].scene.children[0].remove(this.cube[view as keyof Views]); - }); + this.cancelDraw(); } this.model.data.groupData.grouped = []; this.setHelperVisibility(false); 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')); @@ -1154,7 +1173,6 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { return; } if (!this.action.selectable) return; - this.resetColor(); const object = this.views.perspective.scene.getObjectByName(clientID); if (object === undefined) return; this.model.data.focusData.clientID = clientID; @@ -1168,18 +1186,23 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { }), ); } else if (this.model.data.focusData.clientID !== null) { - this.resetColor(); this.model.data.focusData.clientID = null; } } }; private resetColor(): void { + const { opacity, selectedOpacity } = this.model.data.shapeProperties; this.model.data.objects.forEach((object: any): void => { const { clientID } = object; const target = this.views.perspective.scene.getObjectByName(String(clientID)); if (target) { ((target as THREE.Mesh).material as THREE.MeshBasicMaterial).color.set((target as any).originalColor); + if (this.model.data.activeElement.clientID === String(clientID)) { + ((target as THREE.Mesh).material as THREE.MeshBasicMaterial).opacity = selectedOpacity / 100; + } else { + ((target as THREE.Mesh).material as THREE.MeshBasicMaterial).opacity = opacity / 100; + } } }); } 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 08ed0f1eaf7f..c0ab00cead4b 100644 --- a/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper3D.tsx +++ b/cvat-ui/src/components/annotation-page/canvas/canvas-wrapper3D.tsx @@ -55,6 +55,8 @@ interface Props { automaticBordering: boolean; showObjectsTextAlways: boolean; frame: number; + resetZoom : boolean; + canvasBackgroundColor: string; } interface ViewSize { @@ -188,6 +190,8 @@ const CanvasWrapperComponent = (props: Props): ReactElement => { onShapeDrawn, onCreateAnnotations, frameFetching, + resetZoom, + canvasBackgroundColor, } = props; const { canvasInstance } = props as { canvasInstance: Canvas3d }; @@ -374,7 +378,7 @@ const CanvasWrapperComponent = (props: Props): ReactElement => { useEffect(() => { updateShapesView(); - }, [opacity, outlined, outlineColor, selectedOpacity, colorBy]); + }, [opacity, outlined, outlineColor, selectedOpacity, colorBy, resetZoom, canvasBackgroundColor]); useEffect(() => { const canvasInstanceDOM = canvasInstance.html() as ViewsDOM; @@ -386,6 +390,7 @@ const CanvasWrapperComponent = (props: Props): ReactElement => { canvasInstanceDOM.perspective.addEventListener('click', onCanvasClick); canvasInstanceDOM.perspective.addEventListener('canvas.fit', onResize); canvasInstanceDOM.perspective.addEventListener('canvas.groupped', onCanvasObjectsGroupped); + canvasInstanceDOM.perspective.addEventListener('canvas.settings', onCanvasShapeDrawn); window.addEventListener('resize', onResize); return () => { @@ -396,6 +401,7 @@ const CanvasWrapperComponent = (props: Props): ReactElement => { canvasInstanceDOM.perspective.removeEventListener('click', onCanvasClick); canvasInstanceDOM.perspective.removeEventListener('canvas.fit', onResize); canvasInstanceDOM.perspective.removeEventListener('canvas.groupped', onCanvasObjectsGroupped); + canvasInstanceDOM.perspective.removeEventListener('canvas.settings', onCanvasShapeDrawn); window.removeEventListener('resize', onResize); }; }, [frameData, annotations, activeLabelID, contextMenuVisibility]);