diff --git a/CHANGELOG.md b/CHANGELOG.md index f06d8de4..fa8174d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ from online detectors & interactors) ( ### Changed - `api/docs`, `api/swagger`, `api/schema`, `server/about` endpoints now allow unauthorized access (, ) +- 3D canvas now can be dragged in IDLE mode () - Datumaro version is upgraded to 0.3 (dev) () - Allowed trailing slashes in the SDK host address () - Enabled authentication via email () diff --git a/cvat-canvas3d/package.json b/cvat-canvas3d/package.json index 487e21de..fc5f1b30 100644 --- a/cvat-canvas3d/package.json +++ b/cvat-canvas3d/package.json @@ -1,6 +1,6 @@ { "name": "cvat-canvas3d", - "version": "0.0.1", + "version": "0.0.2", "description": "Part of Computer Vision Annotation Tool which presents its canvas3D library", "main": "src/canvas3d.ts", "scripts": { diff --git a/cvat-canvas3d/src/typescript/canvas3dView.ts b/cvat-canvas3d/src/typescript/canvas3dView.ts index e875e420..120ed5b2 100644 --- a/cvat-canvas3d/src/typescript/canvas3dView.ts +++ b/cvat-canvas3d/src/typescript/canvas3dView.ts @@ -1,4 +1,5 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT @@ -76,6 +77,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { private cube: CuboidModel; private highlighted: boolean; private selected: CubeObject; + private isPerspectiveBeingDragged: boolean; private model: Canvas3dModel & Master; private action: any; private globalHelpers: any; @@ -94,6 +96,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { this.speed = CONST.MOVEMENT_FACTOR; this.cube = new CuboidModel('line', '#ffffff'); this.highlighted = false; + this.isPerspectiveBeingDragged = false; this.selected = this.cube; this.model = model; this.globalHelpers = { @@ -249,6 +252,15 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { } }); + canvasPerspectiveView.addEventListener('mousedown', this.onPerspectiveDrag); + window.document.addEventListener('mouseup', () => { + this.disablePerspectiveDragging(); + if (this.isPerspectiveBeingDragged && this.mode !== Mode.DRAG_CANVAS) { + // call this body only of drag was activated inside the canvas, but not globally + this.isPerspectiveBeingDragged = false; + } + }); + canvasTopView.addEventListener('mousedown', this.startAction.bind(this, 'top')); canvasSideView.addEventListener('mousedown', this.startAction.bind(this, 'side')); canvasFrontView.addEventListener('mousedown', this.startAction.bind(this, 'front')); @@ -276,8 +288,11 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { canvasPerspectiveView.addEventListener('click', (e: MouseEvent): void => { e.preventDefault(); - if (e.detail !== 1) return; - if (![Mode.GROUP, Mode.IDLE].includes(this.mode) || !this.views.perspective.rayCaster) return; + const selectionIsBlocked = ![Mode.GROUP, Mode.IDLE].includes(this.mode) || + !this.views.perspective.rayCaster || + this.isPerspectiveBeingDragged; + + if (e.detail !== 1 || selectionIsBlocked) return; const intersects = this.views.perspective.rayCaster.renderer.intersectObjects( this.views.perspective.scene.children[0].children, false, @@ -450,7 +465,6 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { 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; @@ -528,6 +542,30 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { } } + private enablePerspectiveDragging(): void { + const { controls } = this.views.perspective; + controls.mouseButtons.left = CameraControls.ACTION.ROTATE; + controls.mouseButtons.right = CameraControls.ACTION.TRUCK; + controls.touches.one = CameraControls.ACTION.TOUCH_ROTATE; + controls.touches.two = CameraControls.ACTION.TOUCH_DOLLY_TRUCK; + controls.touches.three = CameraControls.ACTION.TOUCH_TRUCK; + } + + private disablePerspectiveDragging(): void { + const { controls } = this.views.perspective; + controls.mouseButtons.left = CameraControls.ACTION.NONE; + controls.mouseButtons.right = CameraControls.ACTION.NONE; + controls.touches.one = CameraControls.ACTION.NONE; + controls.touches.two = CameraControls.ACTION.NONE; + controls.touches.three = CameraControls.ACTION.NONE; + } + + private onPerspectiveDrag = (): void => { + if (![Mode.DRAG_CANVAS, Mode.IDLE].includes(this.mode)) return; + this.isPerspectiveBeingDragged = true; + this.enablePerspectiveDragging(); + } + private startAction(view: any, event: MouseEvent): void { if (event.detail !== 1) return; if (this.model.mode === Mode.DRAG_CANVAS) return; @@ -844,24 +882,26 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { } else if (reason === UpdateReasons.OBJECTS_UPDATED) { this.setupObjects(); } else if (reason === UpdateReasons.DRAG_CANVAS) { + this.isPerspectiveBeingDragged = true; this.dispatchEvent( - new CustomEvent(this.model.mode === Mode.DRAG_CANVAS ? 'canvas.dragstart' : 'canvas.dragstop', { + new CustomEvent('canvas.dragstart', { 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.DRAG_CANVAS) { + this.isPerspectiveBeingDragged = false; + this.dispatchEvent( + new CustomEvent('canvas.dragstop', { + bubbles: false, + cancelable: true, + }), + ); + } + if (this.mode === Mode.DRAW) { this.controller.drawData.enabled = false; this.controller.drawData.redraw = undefined; @@ -869,16 +909,12 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { this.views[view as keyof Views].scene.children[0].remove(this.cube[view as keyof Views]); }); } + 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')); @@ -991,6 +1027,10 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { // eslint-disable-next-line no-param-reassign points.material.size = 0.05; points.material.color.set(new THREE.Color(0xffffff)); + + const { controls } = this.views.perspective; + controls.mouseButtons.wheel = CameraControls.ACTION.DOLLY; + const material = points.material.clone(); const sphereCenter = points.geometry.boundingSphere.center; const { radius } = points.geometry.boundingSphere; @@ -1175,7 +1215,7 @@ export class Canvas3dViewImpl implements Canvas3dView, Listener { this.cube.perspective.position.copy(newPoints); this.views.perspective.renderer.domElement.style.cursor = 'default'; } - } else if (this.mode === Mode.IDLE) { + } else if (this.mode === Mode.IDLE && !this.isPerspectiveBeingDragged) { const { children } = this.views.perspective.scene.children[0]; const { renderer } = this.views.perspective.rayCaster; const intersects = renderer.intersectObjects(children, false); diff --git a/tests/cypress/integration/canvas3d_functionality/case_86_canvas3d_functionality_move_image_button.js b/tests/cypress/integration/canvas3d_functionality/case_86_canvas3d_functionality_move_image_button.js index 0018a901..6aef5f17 100644 --- a/tests/cypress/integration/canvas3d_functionality/case_86_canvas3d_functionality_move_image_button.js +++ b/tests/cypress/integration/canvas3d_functionality/case_86_canvas3d_functionality_move_image_button.js @@ -1,7 +1,10 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) CVAT.ai Corporation // // SPDX-License-Identifier: MIT +/* eslint-disable cypress/no-unnecessary-waiting */ + /// import { taskName, labelName } from '../../support/const_canvas3d'; @@ -9,12 +12,10 @@ import { taskName, labelName } from '../../support/const_canvas3d'; context('Canvas 3D functionality. "Move the image" button interaction.', () => { const caseId = '86'; const screenshotsPath = 'cypress/screenshots/canvas3d_functionality/case_86_canvas3d_functionality_move_image_button.js'; - const cuboidCreationParams = { - labelName: labelName, - }; + const cuboidCreationParams = { labelName }; before(() => { - cy.openTask(taskName) + cy.openTask(taskName); cy.openJob(); cy.wait(1000); // Waiting for the point cloud to display cy.create3DCuboid(cuboidCreationParams); diff --git a/tests/cypress/integration/canvas3d_functionality_2/case_56_canvas3d_functionality_basic_actions.js b/tests/cypress/integration/canvas3d_functionality_2/case_56_canvas3d_functionality_basic_actions.js index bbeeed85..d54f162b 100644 --- a/tests/cypress/integration/canvas3d_functionality_2/case_56_canvas3d_functionality_basic_actions.js +++ b/tests/cypress/integration/canvas3d_functionality_2/case_56_canvas3d_functionality_basic_actions.js @@ -1,7 +1,10 @@ // Copyright (C) 2021-2022 Intel Corporation +// Copyright (C) 2022 CVAT.ai Corporation // // SPDX-License-Identifier: MIT +/* eslint-disable cypress/no-unnecessary-waiting */ + /// import { taskName } from '../../support/const_canvas3d'; @@ -75,9 +78,33 @@ context('Canvas 3D functionality. Basic actions.', () => { before(() => { cy.openTaskJob(taskName); + cy.wait(2000); // Waiting for the point cloud to display }); describe(`Testing case "${caseId}"`, () => { + it.skip('Check canvas can be zoomed.', () => { + // after some investigations it is clear that tests do not work for 3D + // in headless mode, need more time to investigate + const screenshotNameBefore = 'before_idle_zoom'; + const screenshotNameAfter = 'after_idle_zoom'; + cy.screenshot(screenshotNameBefore, { + onAfterScreenshot: () => { + cy.get('.cvat-canvas3d-perspective').should('exist').and('be.visible') + .within(() => { + cy.get('canvas').trigger('wheel', { deltaY: -500 }); + cy.screenshot(screenshotNameAfter, { + onAfterScreenshot: () => { + cy.compareImagesAndCheckResult( + `${screenshotsPath}/${screenshotNameBefore}.png`, + `${screenshotsPath}/${screenshotNameAfter}.png`, + ); + }, + }); + }); + }, + }); + }); + it('Check existing of elements.', () => { cy.get('.cvat-canvas3d-perspective') .should('exist')